10 Declarations [dcl.dcl]

10.1 Specifiers [dcl.spec]

10.1.7 Type specifiers [dcl.type] Simple type specifiers [dcl.type.simple]

The simple type specifiers are

	nested-name-specifieropt type-name
	nested-name-specifier template simple-template-id
	nested-name-specifieropt template-name
	decltype ( expression )
	decltype ( auto )

The simple-type-specifier auto is a placeholder for a type to be deduced ([dcl.spec.auto]). A type-specifier of the form typenameopt nested-name-specifieropt template-name is a placeholder for a deduced class type ([dcl.type.class.deduct]). The template-name shall name a class template that is not an injected-class-name. The other simple-type-specifiers specify either a previously-declared type, a type determined from an expression, or one of the fundamental types. Table 11 summarizes the valid combinations of simple-type-specifiers and the types they specify.

Table 11simple-type-specifiers and the types they specify
Specifier(s) Type
type-name the type named
simple-template-id the type as defined in [temp.names]
template-name placeholder for a type to be deduced
char “char”
unsigned char “unsigned char”
signed char “signed char”
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”
auto placeholder for a type to be deduced
decltype(auto) placeholder for a type to be deduced
decltype(expression) the type as defined below

When multiple simple-type-specifiers are allowed, they can be freely intermixed with other decl-specifiers in any order. [Note: It is implementation-defined whether objects of char type are represented as signed or unsigned quantities. The signed specifier forces char objects to be signed; it is redundant in other contexts. end note]

For an expression e, the type denoted by decltype(e) is defined as follows:

The operand of the decltype specifier is an unevaluated operand.


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] [Note: The rules for determining types involving decltype(auto) are specified in [dcl.spec.auto]. end note]

If the operand of a decltype-specifier is a prvalue, the temporary materialization conversion is not applied and no result object is provided for the prvalue. The type of the prvalue may be incomplete. [Note: As a result, storage is not allocated for the prvalue and it is not destroyed. Thus, a class type is not instantiated as a result of being the type of a function call in this context. In this context, the common purpose of writing the expression is merely to refer to its type. In that sense, a decltype-specifier is analogous to a use of a typedef-name, so the usual reasons for requiring a complete type do not apply. In particular, it is not necessary to allocate storage for a temporary object or to enforce the semantic constraints associated with invoking the type's destructor. end note] [Note: Unlike the preceding rule, parentheses have no special meaning in this context. end note] [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 T=int]”.
                                // The return type is A<int>, so a temporary is introduced and its
                                // destructor is used, so the program is ill-formed.

end example]