Przestrzenie nazw
Warianty
Działania

Typy

Z cppreference.com
< cpp‎ | language

Obiekty, referencje, funkcje - w tym specjalizacje szablonów funkcyjnych, oraz wyrażenia mają właściwość zwaną typem, która zarówno zawęża dopuszczalne operacje dla tych bytów, jak i nadaje znaczenie tam, gdzie w przeciwnym wypadku mielibyśmy do czynienia tylko z sekwencją bitów.

Spis treści

[edytuj] Klasyfikacja typów

System typów języka C++ składa się z następujących typów:

  • typy zmiennoprzecinkowe float, double, long double (zob. std:is_floating_point);
  • typy całkowitoloczbowe (zob. std::is_integral):
  • typ logiczny bool;
  • typy znakowe:
  • wąskie typy znakowe (char, signed char, unsigned char);
  • szerokie typy znakowe (char16_t, char32_t, wchar_t);
  • typy całkowitoliczbowe ze znakiem (short int, int, long int, long long int);
  • typy całkowitoliczbowe bez znaku (unsigned short int, unsigned int, unsigned long int, unsigned long long int);
  • referencja do l-wartości będącej typem obiektowym;
  • referencja do l-wartości będącej typem funkcyjnym;
  • referencja do r-wartości będącej typem obiektowym;
  • referencja do r-wartości będącej typem funkcyjnym;

Dla wszystkich typów innych niż referencje i funkcje, system typów wspiera trzy dodatkowe warianty takiego typu: (const, volatile, and const volatile) (zob. znaczniki 'cv')

Typy możemy pogrupować w różne kategorie w zależności od ich właściwości:

[edytuj] Nazewnictwo typów

Nazwa może być zadeklarowana jako odnosząca się do:

Programy języka C++ często potrzebują odwołać się do typów nie posiadających nazw. Składnia pozwalająca na takie odwołanie nazywa się "identyfikatorem typu" - type-id. Składnia identyfikatora typu nazywającego typ T jest identyczna jak składnia deklaracji zmiennej lub funkcji typu T, z pominiętym identyfikatorem.

Wyjątek od powyższej reguły: decl-specifier-seq of the declaration grammar is constrained to type-specifier-seq, and that new types may be defined only if the type-id appears on the right-hand side of a non-template type alias declaration.

int* p;               // deklaracja typu 'wskaźnik do int'
static_cast<int*>(p); // type-id wskaźnika do int
 
int a[3];   // deklaracja tablicy 3 elementów typu int
new int[3]; // type-id tablicy 3 elementów int (nazywany new-type-id)
 
int (*(*x[2])())[3];      // deklaracja tablicy dwóch wskaźników do funkcji
                          // zwracających wskaźnik do tablicy 3 elementów typu int      
new (int (*(*[2])())[3]); // type-id to "int (*(*[2])())[3]"
 
void f(int);                    // dekaracja funkcji przyjmującej argument int
                                // i zwracającej void
std::function<void(int)> x = f; // parametr szablonu o type-id "void(int)"
 
std::vector<int> v;       // deklaracja wektora zawierającego int
sizeof(std::vector<int>); // type-id to "std::vector<int>"
 
struct { int x; } b;         // tworzy nowy anonimowy typ i deklaruje zmienną b tego typu
sizeof(struct{ int x; });    // bład: nie można zdefiniować nowego typu w wyrażeniu sizeof
using t = struct { int x; }; // tworzy nowy anonimowy typ i deklaruje dla niego alias 't'
 
sizeof(static int); // bład: znacznik czasu przechowywania 
                    // nie jest częścią type-specifier-seq
std::function<inline void(int)> f; // błąd: znaczniki funkcji również 
                                   //nie są częścią type-specifier-seq

W przypadku usunięcia z sekcji declarator w gramatyce deklaracji nazwy deklarownego bytu, otrzymujemy wyrażenie nazywane abstract-declarator.

Type-id może być użyte w następujących sytuacjach:

Type-id może, z pewnymi modyfikacjami, być użyte w następujących sytuacjach:

  • na liście parametrów funkcji, jeśli nazwa parametru jest pominięta, type-id używa reguły gramatyki decl-specifier-seq zamiast type-specifier-seq ( w szczególności, dopuszczalne są niektóre znaczniki czasu przechowywania);
  • w nazwie definiowanego przez użytkownika operatora rzutowania, abstract-declarator nie może zawierać operatorów wywołania funkcji ani dostępu do tablicy.

[edytuj] Rozbudowany znacznik typu

Rozbudowany znacznik typu może być użyty do odniesienia się do wcześniej zadeklarowanej nazwy klasy, struktury unii lub enumeracji nawet jeśli ta nazwa była przez inną deklarację bytu nie będącego typem (zob. wyszukiwanie nazw). Takie znaczniki mogą być również użyte do zadeklarowania nowej nazwy klasy.

Bardziej szczegółowe wyjaśnienie w: rozbudowany znacznik typu.

[edytuj] Typ statyczny

Typ wyrażenia może być wynikiem analizy programu przeprowadzonej w czasie kompilacji. Taki typ nazywamy wówczas typem statycznym wyrażenia. Typ statyczny nie może ulec zmianie w trakcie wykonania programu.

[edytuj] Typ dynamiczny

Jeżeli wyrażenie będące gl-wartością odnosi się do polimorficznego obiektu, typ ostatniego obiektu w hierarchi dziedziczenia nazywamy typem dynamicznym.

// jeżeli dane są
struct B { virtual ~B() {} }; // typ polimorficzny
struct D: B {}; // typ polimorficzny
D d; // ostatni obiekt w hierarchi dziedziczenia
B* ptr = &d;
// typ statyczny wyrażenia (*ptr) to B
// typ dynamiczny wyrażenia (*ptr) to D


Dla wyrażeń będących pr-wartościami, typ dynamiczny jest zawsze identyczny z typem statycznym.

[edytuj] Typy niekompletne

Następujące typy nazywamy typami niekompletnymi:

W kontekstach określonych poniżej wymagane jest, by klasa T była kompletna:

Mówiąc ogólnie, w powyższych przypadkach musi być znany rozmiar i wyrównanie (ang. alignment) typu T.

Jeżeli którykolwiek z tych przypadków zachodzi w danej jednostce translacji, definicja typu T również musi pojawić się w tej jednosce translacji. W przeciwnym przypadku nie jest to wymagane.

[edytuj] See also