C++11

Sablonok

Extern template

C++03-ban ha egy teljesen specializált template előfordult egy fordítási egységben, akkor a fordítónak kötelező volt a fordítási egységen belül azt külön példányosítania. Ez rossz esetben nagyon megnövelhette a fordítási időt.

A C++11 engedélyezi extern kulcsszó használatát a template-ekre, mellyel jelezhetjük a compilernek, hogy ne példányosítsa az adott template-et az adott fordítási egységben:

extern template class std::vector<MyClass>;

Template aliasok

A C++03-ban a typedef kulcsszóval csak konkrét típusokhoz rendelhetünk szinonimát, azonban nincs lehetőség sablonok néhány paraméterét lekötve új sablonokat származtatni. Az új szabvány bevezet egy ilyen konstrukciós lehetőséget a következő szintaxissal:

template <typename First, typename Second, int third> class SomeType; template <typename Second> using TypedefName = SomeType<OtherType, Second, 5>;

Variadic template

C-ben és C++-ban régóta használhatunk változó számú argumentumú függvényeket. Template-ek esetén viszont csak fix számú template-paraméter volt eddig megengedett:

template<typename T> struct S { S(T t) { } };

Ekkor S<int> esetén a következő generálódik:

struct S { S(int t) { }; };

Ez esetben S<>, S<int, char> nem fordul, mivel S-nek pontosan 1 paraméterre van szüksége.

Az új C++-ban a variadic template használatával már ez a korlátozás sem létezik:

template<typename... T> struct S { S(T... t) { } };

Ekkor S<int> továbbra is a következőt jelenti:

struct S { S(int t) { }; };

S<> esetén:

struct S { S( ) { } };

S<int, char> esetén pedig:

struct S { S(int t1, char t2) { } };

Sablon-függvények definíciója variadic template használatával legegyszerűbben rekurzióval történhet meg. A függvényt külön definiáljuk a nulla, és a nem nulla argumentumú esetekre. A működés rekurzív, ha feldolgoztunk egy argumentumot, meghívjuk az eggyel kisebb számú argumentumú változatot:

void printf(const char *s) { // nulla argumentum } template<typename T, typename... Args> void printf(const char* s, T value, Args... args) { // az első argumentumot leválasztottuk, // a feldolgozása után a meghívjuk magunkat, hogy // a többi is fel legyen dolgozva: printf(s, args...); }

Nézzünk még egy példát a variadic template-ekre rekurzió használatával:

void printf(const char *s) { while (*s) { if (*s == '%' && *(++s) != '%') throw std::runtime_error("invalid format string: missing arguments"); std::cout << *s++; } } template<typename T, typename... Args> void printf(const char* s, T value, Args... args) { while (*s) { if (*s == '%' && *(++s) != '%') { std::cout << value; printf(*s ? ++s : s, args...); return; } std::cout << *s++; } throw std::logic_error("extra arguments provided to printf"); }

Ezekután például

alma = new Alma(); korte= new Korte(); printf(„alma=%a, korte=%b ”, alma, korte);

Először:

printf(const char*, Alma alma, Args... args);

A rekurzív függvényhívás:

printf(const char*, Korte korte, Args... args)

Az utolsó rekurzív hívás:

printf(const char *s)

Így lehetséges rekurzívan végiggyalogolni az Args… -on

Concept

A szabványalkotó bizottság kivette a concepteket a készülő új szabvány tervéből, azok biztos, hogy nem lesznek benne a megjelenésekor. Indoklásukban lényegében azt mondják, hogy a módosításra az iparnak nincs szüksége, és a C++ szellemisége mindig is a gyakorlati tapasztalatokat helyezte előtérbe. Stroustrup a throws listához hasonlította, amely elméleti megfontolásokból került bele a nyelvbe, és határozottan nem aratott sikert, ezt nem akarták megismételni.

Az itt bemutatott verzió az eredeti terveket mutatja. A számos, conceptekre építő egyéb kiegészítés sorsa még nem tisztázott.

A conceptek bevezetésével a C++ is támogatja a korlátozott parametrikus polimorfizmust. Sablonok definiálásakor a paraméterekre megkötéseket tehetünk, ezeket a megkötéseket nevezzük koncepcióknak. Például a min függvény használatához a típuson kell, hogy legyen rendezés operátor, ezt kifejezhetjük a LessThanComparable koncepcióval:

template<LessThanComparable T> const T& min(const T &x, const T &y) { return y < x ? y : x; }

A LessThanComparable koncepció része lesz a szabványos könyvtárnak, így definiálva:

auto concept LessThanComparable<typename T> { bool operator<(T, T); }

Ebben a definícióban azt mondjuk, hogy egy típus megfelel a LessThanComparable koncepciónak, ha van megfelelő szignatúrájú < operátora. Az auto kulcsszó az elején azt jelenti, hogy azt, hogy a típus megfelel-e a koncepciónak automatikusan ellenőrizzük. Enélkül a típusokat explicit a koncepcióhoz kellene kötnünk a concept_map kulcsszóval.

Egy koncepciónak nem csak egy típusparamétere lehet. A Convertible koncepció például azt fejezi ki, hogy egy típus egy másikra konvertálható. Ezt így definiálhatjuk:

auto concept Convertible<typename T, typename U> { operator U(const T&); }

Ezt a koncepciót csak az általános szintaxissal tudjuk használni:

template<typename U, typename T> requires Convertible<T, U> U convert(const T& t) { return t; }

Amennyiben a concept nem auto, vagy a mi típusunk az abban kikötötteket máshogy kívánja teljesíteni, akkor használhatjuk a concept_map-et, ami egy fordítási idejű leképezése a típusok deklarációinak a conceptek előírásaira. Például fordított rendezést érhetünk el, ha az alábbi deklarációt tesszük:

concept_map LessThanComparable { bool operator<(const MyTypeWithLessThan& l, const MyTypeWithLessThan& r) { return r < l; } }