type-specifier: simple-type-specifier elaborated-type-specifier typename-specifier cv-qualifier
type-specifier-seq: type-specifier attribute-specifier-seq type-specifier type-specifier-seq
defining-type-specifier: type-specifier class-specifier enum-specifier
defining-type-specifier-seq: defining-type-specifier attribute-specifier-seq defining-type-specifier defining-type-specifier-seq
const int ci = 3; // cv-qualified (initialized as required) ci = 4; // error: attempt to modify const int i = 2; // not cv-qualified const int* cip; // pointer to const int cip = &i; // OK: cv-qualified access path to unqualified *cip = 4; // error: attempt to modify through ptr to const int* ip; ip = const_cast<int*>(cip); // cast needed to convert const int* to int* *ip = 4; // defined: *ip points to i, a non-const object const int* ciq = new const int (3); // initialized as required int* iq = const_cast<int*>(ciq); // cast required *iq = 4; // undefined behavior: modifies a const object
struct X { mutable int i; int j; }; struct Y { X x; Y(); }; const Y y; y.x.i++; // well-formed: mutable member can be modified y.x.j++; // error: const-qualified member modified Y* p = const_cast<Y*>(&y); // cast away const-ness of y p->x.i = 99; // well-formed: mutable member can be modified p->x.j = 99; // undefined behavior: modifies a const subobject
simple-type-specifier: nested-name-specifier type-name nested-name-specifier template simple-template-id decltype-specifier placeholder-type-specifier nested-name-specifier template-name char char8_t char16_t char32_t wchar_t bool short int long signed unsigned float double void
type-name: class-name enum-name typedef-name
typename nested-name-specifier template simple-template-idwhere the nested-name-specifier (if any) is non-dependent and the template-name of the simple-template-id names a deducible template.
Specifier(s) | Type |
type-name | the type named |
simple-template-id | the type as defined in [temp.names] |
decltype-specifier | the type as defined in [dcl.type.decltype] |
placeholder-type-specifier | the type as defined in [dcl.spec.auto] |
template-name | the type as defined in [dcl.type.class.deduct] |
char | “char” |
unsigned char | “unsigned char” |
signed char | “signed char” |
char8_t | “char8_t” |
char16_t | “char16_t” |
char32_t | “char32_t” |
bool | “bool” |
unsigned | “unsigned int” |
unsigned int | “unsigned int” |
signed | “int” |
signed int | “int” |
int | “int” |
unsigned short int | “unsigned short int” |
unsigned short | “unsigned short int” |
unsigned long int | “unsigned long int” |
unsigned long | “unsigned long int” |
unsigned long long int | “unsigned long long int” |
unsigned long long | “unsigned long long int” |
signed long int | “long int” |
signed long | “long int” |
signed long long int | “long long int” |
signed long long | “long long int” |
long long int | “long long int” |
long long | “long long int” |
long int | “long int” |
long | “long int” |
signed short int | “short int” |
signed short | “short int” |
short int | “short int” |
short | “short int” |
wchar_t | “wchar_t” |
float | “float” |
double | “double” |
long double | “long double” |
void | “void” |
elaborated-type-specifier: class-key attribute-specifier-seq nested-name-specifier identifier class-key simple-template-id class-key nested-name-specifier template simple-template-id elaborated-enum-specifier
elaborated-enum-specifier: enum nested-name-specifier identifier
class-key attribute-specifier-seq identifier ; friend class-key :: identifier ; friend class-key :: simple-template-id ; friend class-key nested-name-specifier identifier ; friend class-key nested-name-specifier template simple-template-id ;
friend class T;is ill-formed.
enum class E { a, b }; enum E x = E::a; // OK struct S { } s; class S* p = &s; // OK— end example
decltype-specifier: decltype ( expression )
const int&& foo(); int i; struct A { double x; }; const A* a = new A(); decltype(foo()) x1 = 17; // type is const int&& decltype(i) x2; // type is int decltype(a->x) x3; // type is double decltype((a->x)) x4 = x3; // type is const double&— end example
template<class T> struct A { ~A() = delete; }; template<class T> auto h() -> A<T>; template<class T> auto i(T) // identity -> T; template<class T> auto f(T) // #1 -> decltype(i(h<T>())); // forces completion of A<T> and implicitly uses A<T>::~A() // for the temporary introduced by the use of h(). // (A temporary is not introduced as a result of the use of i().) template<class T> auto f(T) // #2 -> void; auto g() -> void { f(42); // OK: calls #2. (#1 is not a viable candidate: type deduction // fails ([temp.deduct]) because A<int>::~A() is implicitly used in its // decltype-specifier) } template<class T> auto q(T) -> decltype((h<T>())); // does not force completion of A<T>; A<T>::~A() is not implicitly // used within the context of this decltype-specifier void r() { q(42); // error: deduction against q succeeds, so overload resolution selects // the specialization “q(T) -> decltype((h<T>()))” with Tint; // the return type is A<int>, so a temporary is introduced and its // destructor is used, so the program is ill-formed }— end example
placeholder-type-specifier: type-constraint auto type-constraint decltype ( auto )
( expression-list )the expression-list shall be a single assignment-expression.
auto x = 5; // OK: x has type int const auto *v = &x, u = 6; // OK: v has type const int*, u has type const int static auto y = 0.0; // OK: y has type double auto int r; // error: auto is not a storage-class-specifier auto f() -> int; // OK: f returns int auto g() { return 0.0; } // OK: g returns double auto h(); // OK: h's return type will be deduced when it is defined— end example
auto x = 5, *y = &x; // OK: auto is int auto a = 5, b = { 1, 2 }; // error: different types for auto— end example
auto f() { } // OK, return type is void auto* g() { } // error: cannot deduce auto* from void()— end example
auto n = n; // error: n's initializer refers to n auto f(); void g() { &f; } // error: f's return type is unknown auto sum(int i) { if (i == 1) return i; // sum's return type is int else return sum(i-1)+i; // OK, sum's return type has been deduced }— end example
template <class T> auto f(T t) { return t; } // return type deduced at instantiation time typedef decltype(f(1)) fint_t; // instantiates f<int> to deduce return type template<class T> auto f(T* t) { return *t; } void g() { int (*p)(int*) = &f; } // instantiates both fs to determine return types, // chooses second— end example
auto f(); auto f() { return 42; } // return type is int auto f(); // OK int f(); // error: cannot be overloaded with auto f() decltype(auto) f(); // error: auto and decltype(auto) don't match template <typename T> auto g(T t) { return t; } // #1 template auto g(int); // OK, return type is int template char g(char); // error: no matching template template<> auto g(double); // OK, forward declaration with unknown return type template <class T> T g(T t) { return t; } // OK, not functionally equivalent to #1 template char g(char); // OK, now there is a matching template template auto g(float); // still matches #1 void h() { return g(42); } // error: ambiguous template <typename T> struct A { friend T frf(T); }; auto frf(int i) { return i; } // not a friend of A<int> extern int v; auto v = 17; // OK, redeclares v struct S { static int i; }; auto S::i = 23; // OK— end example
template <typename T> auto f(T t) { return t; } extern template auto f(int); // does not instantiate f<int> int (*p)(int) = f; // instantiates f<int> to determine its return type, but an explicit // instantiation definition is still required somewhere in the program— end example
auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int> auto x2 = { 1, 2.0 }; // error: cannot deduce element type auto x3{ 1, 2 }; // error: not a single element auto x4 = { 3 }; // decltype(x4) is std::initializer_list<int> auto x5{ 3 }; // decltype(x5) is int— end example
const auto &i = expr;
template <class U> void f(const U& u);
int i; int&& f(); auto x2a(i); // decltype(x2a) is int decltype(auto) x2d(i); // decltype(x2d) is int auto x3a = i; // decltype(x3a) is int decltype(auto) x3d = i; // decltype(x3d) is int auto x4a = (i); // decltype(x4a) is int decltype(auto) x4d = (i); // decltype(x4d) is int& auto x5a = f(); // decltype(x5a) is int decltype(auto) x5d = f(); // decltype(x5d) is int&& auto x6a = { 1, 2 }; // decltype(x6a) is std::initializer_list<int> decltype(auto) x6d = { 1, 2 }; // error: { 1, 2 } is not an expression auto *x7a = &i; // decltype(x7a) is int* decltype(auto)*x7d = &i; // error: declared type is not plain decltype(auto)— end example
template <class ...T> struct A { A(T...) {} }; A x[29]{}; // error: no declarator operators allowed const A& y{}; // error: no declarator operators allowed— end example
template<class T> struct container { container(T t) {} template<class Iter> container(Iter beg, Iter end); }; template<class Iter> container(Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>; std::vector<double> v = { /* ... */ }; container c(7); // OK, deduces int for T auto d = container(v.begin(), v.end()); // OK, deduces double for T container e{5, 6}; // error: int is not an iterator— end example