C++0x

Típusok, típuskonstrukciók

Elemi típusok

Az előző fejezetben láthattuk, hogy új típusok kerültek bevezetésre, az UTF-16 és UTF-32 karakterláncok tárolására: char16_t és char32_t. Emellett a a legtöbb fordító által már régóta támogatott long long is a szabványba került.

long long

A C99-ben bevezetett long long int típust most már a C++ is fogja támogatni. A szabvány szerint legalább 64 biten ábrázolódik.

Felsorolási típus

A C++ bevezet egy új felsorolási típust is. Az eddigi felsorolási típus ugyanis valójában egész szám volt. Ez a C++03-ban kezdett megváltozni: azóta az enum típusok nem implicit konvertálódnak egymás között, illetve egészre. Nem definiálja a szabvány azt az egész típust sem, amelyen a felsorolási típusok ábrázolódtak, a méretük a fordítótól függ. Nem lehetett továbbá egy névtérben két olyan felsorolási típus, amelyek azonos nevű értéket tartalmaznak, ugyanis az értékek nevei a típust tartalmazó névtérbe kerülnek.

A készülő szabvány szerint a tároláshoz használt egész típus megadható (alapértelmezett az int) és saját névterük van:

enum class Enumeration: unsigned short { Val1, Val2, Val3 = 100, Val4 /* = 101 */, };

A kompatibilitás megtartása miatt az enum kulcsszó továbbra is használható, valamint a definiált nevek a tartalmazó névtérbe is bekerülnek, de saját névtér is tartozik hozzá. Itt is megadható a tárolási típus.

enum Enumeration: unsigned short { Val1, Val2, Val3 = 100, Val4 /* = 101 */, };

Mivel az enum tárolási mérete a benne lévő adatoktól függ, nem volt forward deklarálható. Mivel most már megadható (illetve az enum class esetében ismert), lehetővé válik a forward deklarációjuk:

enum Enum1; // Hibás, nem ismert a mérete. enum Enum2 : unsigned int; // Helyes. enum class Enum3; // Helyes, intben tárolódik. enum class Enum4: unsigned int; // Helyes.

Explicit típuskonverziós operátorok

A szabványos C++ az explicit kulcsszót használja annak jelölésére, hogy egy konstruktor nem használható implicit konverzióra. Az új szabvány megengedi, hogy a kulcsszót konverziós operátorokon is használjuk.

A régi C++ implicit és explicit konstruktorok használatára nyújt lehetőséget.

struct S { S(int); }; // "szokásos constructor" implicit konverzió S s1(1); // ok S s2 = 1; // ok void f(S); f(1); // ok (de néha csúnya meglepetés is lehet - mi van, ha S vector?) struct E { explicit E(int); }; // explicit constructor E e1(1); // ok E e2 = 1; // hiba! void f(E); f(1); // hiba

Azonban nem a konstruktor az egyetlen lehetőség a konverzióra. Ha nincs lehetőségünk megváltoztatni egy osztályt, akkor definiálhatunk egy konverziós operátort egy másik osztályból. Például:

struct S { S(int) { } /* ... */ }; struct SS { int m; SS(int x) :m(x) { } operator S() { return S(m); } // mert S -nek nincs S(SS); -ja }; SS ss(1); S s1 = ss; // ok; mint egy implicit constructor S s2(ss); // ok ; mint egy implicit constructor void f(S); f(ss); // ok; mint egy implicit constructor

Sajnálatos módon nincs explicit konverziós operátor. Ezt a hiányosságot a C++0x kijavítja, és megengedi, hogy a konverziós operátorok explicitek legyenek. Például:

struct S { S(int) { } }; struct SS { int m; SS(int x) :m(x) { } explicit operator S() { return S(m); } // mert S -nek nincs S(SS); -ja }; SS ss(1); S s1 = ss; // hiba; mint egy explicit constructor S s2(ss); // ok ; mint egy explicit constructor void f(S); f(ss); // hiba; mint egy explicit constructor

Konstans kifejezések

Az új C++ bevezeti a konstans kifejezés típusokat is a constexpr kulcsszóval. Ezzel a kulcsszóval jelölhetjük, ha egy függvényünk fordítási időben kiszámítható. Ez használható többek között tömbök definiálásakor (a tömb méretének fordítási időben ismertnek kell lennie C++-ban):

constexpr int GetFive() { return 5; } int some_value[GetFive() + 5];

Null pointer

1972-ben a C-ben a 0 konstansnak két jelentése volt, egyrészt jelentette a 0 számot, másrészt a null pointert. A 0 kettős jelentését úgy kezelték, hogy bevezették a NULL szimbólumot, amelyet az előfeldolgozó oldott fel, általában a ((void *)0) kifejezésre. Ez azonban C++ tervezésekor problémákat okozott, hiszen a void * mutatók nem implicit konvertálódnak más mutató típusokra, így a C++-ban ismét a 0 szimbólumot kellett használni null pointerként (tehát a NULL is erre helyettesítődött). Azonban nem oldódott meg minden probléma.

void foo(int); void foo(void *);

Ekkor a foo(NULL) kifejezés a foo(int) függvényt hívja, ami megtévesztő, és egyértelműen nem a programozó szándékát fejezi ki.

Az új C++ szabvány bevezeti a nullptr kulcsszót a null pointer jelölésére. A nullptr típusa olyan, hogy tetszőleges mutató típusra implicit konvertálódik, valamint tetszőleges mutatóval összehasonlítható, nem konvertálódik azonban egész típusokra.

Típuskikövetkeztetés

A C++-ban részeredmények tárolása sokszor nehézkes, hiszen bonyolult típusok fordulhatnak elő benne, főleg a szabványos könyvtárt nagyrészt alkotó template-k esetén. A pontos típus a felhasználó számára sokszor nehezen határozható meg. Az auto kulcsszót használva a típust a fordító kikövetkezteti. A következő két sor ugyan azt csinálja:

for (vector<int>::const_iterator itr = myvec.begin(); itr != myvec.end(); ++itr) for (auto itr = myvec.begin(); itr != myvec.end(); ++itr)

Hasonló módon a decltype kulcsszóval kikövetkeztethetjük kifejezések típusait, és ezt a típust felhasználhatjuk deklarációban:

int someInt; decltype(someInt) otherIntegerVariable = 5;
Ezzel megoldhatóvá válik egy régi dilemma a függvények visszatérési értékével kapcsolatban:
template decltype(U() + T()) add(U u, T t) { return u + t; }
Az új függvénydeklarációs szintaxissal ez még szebb és szélesebb körűen használhatóvá válik.