If a type-name is encountered while parsing a decl-specifier-seq, it is interpreted as part of the decl-specifier-seq if and only if there is no previous type-specifier other than a cv-qualifier in the decl-specifier-seq. The sequence shall be self-consistent as described below. [ Example:
typedef char* Pc; static Pc;
Here, the declaration static Pc is ill-formed because no name was specified for the static variable of type Pc. To get a variable called Pc, a type-specifier (other than const or volatile) has to be present to indicate that the typedef-name Pc is the name being (re)declared, rather than being part of the decl-specifier sequence. For another example,
void f(const Pc); void g(const int Pc);
— end example ]
[ Note: Since signed, unsigned, long, and short by default imply int, a type-name appearing after one of those specifiers is treated as the name being (re)declared. [ Example:
void h(unsigned Pc); void k(unsigned int Pc);
— end example ] — end note ]
7.1.1 Storage class specifiers [dcl.stc]The register specifier shall be applied only to names of variables declared in a block ([stmt.block]) or to function parameters ([dcl.fct.def]). It specifies that the named variable has automatic storage duration ([basic.stc.auto]). A variable declared without a storage-class-specifier at block scope or declared as a function parameter has automatic storage duration by default.
A register specifier is a hint to the implementation that the variable so declared will be heavily used. [ Note: The hint can be ignored and in most implementations it will be ignored if the address of the variable is taken. This use is deprecated (see [depr.register]). — end note ]
The static specifier can be applied only to names of variables and functions and to anonymous unions ([class.union]). There can be no static function declarations within a block, nor any static function parameters. A static specifier used in the declaration of a variable declares the variable to have static storage duration ([basic.stc.static]), unless accompanied by the thread_local specifier, which declares the variable to have thread storage duration ([basic.stc.thread]). A static specifier can be used in declarations of class members; [class.static] describes its effect. For the linkage of a name declared with a static specifier, see [basic.link].
The extern specifier can be applied only to the names of variables and functions. The extern specifier cannot be used in the declaration of class members or function parameters. For the linkage of a name declared with an extern specifier, see [basic.link]. [ Note: The extern keyword can also be used in explicit-instantiations and linkage-specifications, but it is not a storage-class-specifier in such contexts. — end note ]
The linkages implied by successive declarations for a given entity shall agree. That is, within a given scope, each declaration declaring the same variable name or the same overloading of a function name shall imply the same linkage. Each function in a given set of overloaded functions can have a different linkage, however. [ Example:
static char* f(); char* f() { } char* g(); static char* g() { } void h(); inline void h(); inline void l(); void l(); inline void m(); extern void m(); static void n(); inline void n(); static int a; int a; static int b; extern int b; int c; static int c; extern int d; static int d;
— end example ]
The name of a declared but undefined class can be used in an extern declaration. Such a declaration can only be used in ways that do not require a complete class type. [ Example:
struct S; extern S a; extern S f(); extern void g(S); void h() { g(a); f(); }
— end example ]
The mutable specifier can be applied only to names of class data members ([class.mem]) and cannot be applied to names declared const or static, and cannot be applied to reference members. [ Example:
class X { mutable const int* p; mutable int* const q; };
— end example ]
The mutable specifier on a class data member nullifies a const specifier applied to the containing class object and permits modification of the mutable class member even though the rest of the object is const ([dcl.type.cv]).
7.1.2 Function specifiers [dcl.fct.spec]Function-specifiers can be used only in function declarations.
function-specifier: inline virtual explicit
A function declaration ([dcl.fct], [class.mfct], [class.friend]) with an inline specifier declares an inline function. The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism. An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions defined by [dcl.fct.spec] shall still be respected.
A function defined within a class definition is an inline function. The inline specifier shall not appear on a block scope function declaration.93 If the inline specifier is used in a friend declaration, that declaration shall be a definition or the function shall have previously been declared inline.
An inline function shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case ([basic.def.odr]). [ Note: A call to the inline function may be encountered before its definition appears in the translation unit. — end note ] If the definition of a function appears in a translation unit before its first declaration as inline, the program is ill-formed. If a function with external linkage is declared inline in one translation unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required. An inline function with external linkage shall have the same address in all translation units. A static local variable in an extern inline function always refers to the same object. A string literal in the body of an extern inline function is the same object in different translation units. [ Note: A string literal appearing in a default argument is not in the body of an inline function merely because the expression is used in a function call from that inline function. — end note ] A type defined within the body of an extern inline function is the same type in every translation unit.
The virtual specifier shall be used only in the initial declaration of a non-static class member function; see [class.virtual].
The explicit specifier shall be used only in the declaration of a constructor or conversion function within its class definition; see [class.conv.ctor] and [class.conv.fct].
7.1.3 The typedef specifier [dcl.typedef]A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It has the same semantics as if it were introduced by the typedef specifier. In particular, it does not define a new type and it shall not appear in the type-id. [ Example:
using handler_t = void (*)(int); extern handler_t ignore; extern void (*ignore)(int); using cell = pair<void*, cell*>;
— end example ]
In a given non-class scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers. [ Example:
typedef struct s { } s; typedef int I; typedef int I; typedef I I;
— end example ]
In a given class scope, a typedef specifier can be used to redefine any class-name declared in that scope that is not also a typedef-name to refer to the type to which it already refers. [ Example:
struct S { typedef struct A { } A; typedef struct B B; typedef A A; };
— end example ]
If a typedef specifier is used to redefine in a given scope an entity that can be referenced using an elaborated-type-specifier, the entity can continue to be referenced by an elaborated-type-specifier or as an enumeration or class name in an enumeration or class definition respectively. [ Example:
struct S; typedef struct S S; int main() { struct S* p; } struct S { };
— end example ]
In a given scope, a typedef specifier shall not be used to redefine the name of any type declared in that scope to refer to a different type. [ Example:
class complex { }; typedef int complex;
— end example ]
Similarly, in a given scope, a class or enumeration shall not be declared with the same name as a typedef-name that is declared in that scope and refers to a type other than the class or enumeration itself. [ Example:
typedef int complex; class complex { /* ... */ };
— end example ]
If the typedef declaration defines an unnamed class (or enum), the first typedef-name declared by the declaration to be that class type (or enum type) is used to denote the class type (or enum type) for linkage purposes only ([basic.link]). [ Example:
typedef struct { } *ps, S;
— end example ]
7.1.5 The constexpr specifier [dcl.constexpr]The constexpr specifier shall be applied only to the definition of a variable or variable template, the declaration of a function or function template, or the declaration of a static data member of a literal type ([basic.types]). If any declaration of a function, function template, or variable template has a constexpr specifier, then all its declarations shall contain the constexpr specifier. [ Note: An explicit specialization can differ from the template declaration with respect to the constexpr specifier. — end note ] [ Note: Function parameters cannot be declared constexpr. — end note ] [ Example:
constexpr void square(int &x); constexpr int bufsz = 1024; constexpr struct pixel { int x; int y; constexpr pixel(int); }; constexpr pixel::pixel(int a) : x(a), y(x) { square(x); } constexpr pixel small(2); constexpr void square(int &x) { x *= x; } constexpr pixel large(4); int next(constexpr int x) { return x + 1; } extern constexpr int memsz;
— end example ]
A constexpr specifier used in the declaration of a function that is not a constructor declares that function to be a constexpr function. Similarly, a constexpr specifier used in a constructor declaration declares that constructor to be a constexpr constructor. constexpr functions and constexpr constructors are implicitly inline ([dcl.fct.spec]).
The definition of a constexpr function shall satisfy the following constraints:
it shall not be virtual ([class.virtual]);
its return type shall be a literal type;
each of its parameter types shall be a literal type;
its function-body shall be = delete, = default, or a compound-statement that does not contain
an asm-definition,
a goto statement,
a try-block, or
a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.
[ Example:
constexpr int square(int x) { return x * x; } constexpr long long_max() { return 2147483647; } constexpr int abs(int x) { if (x < 0) x = -x; return x; } constexpr int first(int n) { static int value = n; return value; } constexpr int uninit() { int a; return a; } constexpr int prev(int x) { return --x; } constexpr int g(int x, int n) { int r = 1; while (--n > 0) r *= x; return r; }
— end example ]
The definition of a constexpr constructor shall satisfy the following constraints:
the class shall not have any virtual base classes;
each of the parameter types shall be a literal type;
its function-body shall not be a function-try-block;
In addition, either its function-body shall be = delete, or it shall satisfy the following constraints:
either its function-body shall be = default, or the compound-statement of its function-body shall satisfy the constraints for a function-body of a constexpr function;
every non-variant non-static data member and base class sub-object shall be initialized ([class.base.init]);
if the class is a union having variant members ([class.union]), exactly one of them shall be initialized;
if the class is a union-like class, but is not a union, for each of its anonymous union members having variant members, exactly one of them shall be initialized;
for a non-delegating constructor, every constructor selected to initialize non-static data members and base class sub-objects shall be a constexpr constructor;
for a delegating constructor, the target constructor shall be a constexpr constructor.
[ Example:
struct Length { constexpr explicit Length(int i = 0) : val(i) { } private: int val; };
— end example ]
For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression ([expr.const]), the program is ill-formed; no diagnostic required. [ Example:
constexpr int f(bool b) { return b ? throw 0 : 0; } constexpr int f() { return f(true); } struct B { constexpr B(int x) : i(0) { } int i; }; int global; struct D : B { constexpr D() : B(global) { } };
— end example ]
If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is still a constexpr function or constexpr constructor, even though a call to such a function cannot appear in a constant expression. If no specialization of the template would satisfy the requirements for a constexpr function or constexpr constructor when considered as a non-template function or constructor, the template is ill-formed; no diagnostic required.
A call to a constexpr function produces the same result as a call to an equivalent non-constexpr function in all respects except that a call to a constexpr function can appear in a constant expression.
The constexpr specifier has no effect on the type of a constexpr function or a constexpr constructor. [ Example:
constexpr int bar(int x, int y) { return x + y + x*y; } int bar(int x, int y) { return x * 2 + 3 * y; }
— end example ]
A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression ([expr.const]). Otherwise, or if a constexpr specifier is used in a reference declaration, every full-expression that appears in its initializer shall be a constant expression. [ Note: Each implicit conversion used in converting the initializer expressions and each constructor call used for the initialization is part of such a full-expression. — end note ] [ Example:
struct pixel { int x, y; }; constexpr pixel ur = { 1294, 1024 };constexpr pixel origin;
— end example ]
7.1.6 Type specifiers [dcl.type]As a general rule, at most one type-specifier is allowed in the complete decl-specifier-seq of a declaration or in a type-specifier-seq or trailing-type-specifier-seq. The only exceptions to this rule are the following:
const can be combined with any type specifier except itself.
volatile can be combined with any type specifier except itself.
signed or unsigned can be combined with char, long, short, or int.
short or long can be combined with int.
long can be combined with double.
long can be combined with long.
[ Note: Declaring a variable const can affect its linkage ([dcl.stc]) and its usability in constant expressions ([expr.const]). As described in [dcl.init], the definition of an object or subobject of const-qualified type must specify an initializer or be subject to default-initialization. — end note ]
A pointer or reference to a cv-qualified type need not actually point or refer to a cv-qualified object, but it is treated as if it does; a const-qualified access path cannot be used to modify an object even if the object referenced is a non-const object and can be modified through some other access path. [ Note: Cv-qualifiers are supported by the type system so that they cannot be subverted without casting ([expr.const.cast]). — end note ]
Except that any class member declared mutable ([dcl.stc]) can be modified, any attempt to modify a const object during its lifetime ([basic.life]) results in undefined behavior. [ Example:
const int ci = 3; ci = 4; int i = 2; const int* cip; cip = &i; *cip = 4; int* ip; ip = const_cast<int*>(cip); *ip = 4; const int* ciq = new const int (3); int* iq = const_cast<int*>(ciq); *iq = 4;
For another example
struct X { mutable int i; int j; }; struct Y { X x; Y(); }; const Y y; y.x.i++; y.x.j++; Y* p = const_cast<Y*>(&y); p->x.i = 99; p->x.j = 99;
— end example ]
What constitutes an access to an object that has volatile-qualified type is implementation-defined. If an attempt is made to refer to an object defined with a volatile-qualified type through the use of a glvalue with a non-volatile-qualified type, the program behavior is undefined.
[ Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. Furthermore, for some implementations, volatile might indicate that special hardware instructions are required to access the object. See [intro.execution] for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C. — end note ]
7.1.6.2 Simple type specifiers [dcl.type.simple]The auto specifier is a placeholder for a type to be deduced ([dcl.spec.auto]). The other simple-type-specifiers specify either a previously-declared type, a type determined from an expression, or one of the fundamental types ([basic.fundamental]). Table [tab:simple.type.specifiers] summarizes the valid combinations of simple-type-specifiers and the types they specify.
Table
10—
simple-type-specifiersand the types they specify
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:
if e is an unparenthesized id-expression or an unparenthesized class member access ([expr.ref]), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;
otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
otherwise, decltype(e) is the type of e.
The operand of the decltype specifier is an unevaluated operand (Clause [expr]).
[ Example:
const int&& foo(); int i; struct A { double x; }; const A* a = new A(); decltype(foo()) x1 = 0; decltype(i) x2; decltype(a->x) x3; decltype((a->x)) x4 = x3;
— end example ] [ Note: The rules for determining types involving decltype(auto) are specified in [dcl.spec.auto]. — end note ]
[ Note: in the case where the operand of a decltype-specifier is a function call and the return type of the function is a class type, a special rule ([expr.call]) ensures that the return type is not required to be complete (as it would be if the call appeared in a sub-expression or outside of a decltype-specifier). 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. [ Example:
template<class T> struct A { ~A() = delete; }; template<class T> auto h() -> A<T>; template<class T> auto i(T) -> T; template<class T> auto f(T) -> decltype(i(h<T>())); template<class T> auto f(T) -> void; auto g() -> void { f(42); } template<class T> auto q(T) -> decltype((h<T>())); void r() { q(42); }
— end example ] — end note ]
7.1.6.4 auto specifier [dcl.spec.auto]The auto and decltype(auto) type-specifiers designate a placeholder type that will be replaced later, either by deduction from an initializer or by explicit specification with a trailing-return-type. The auto type-specifier is also used to signify that a lambda is a generic lambda.
A program that uses auto or decltype(auto) in a context not explicitly allowed in this section is ill-formed.
When a variable declared using a placeholder type is initialized, or a return statement occurs in a function declared with a return type that contains a placeholder type, the deduced return type or variable type is determined from the type of its initializer. In the case of a return with no operand, the initializer is considered to be void(). Let T be the declared type of the variable or return type of the function. If the placeholder is the auto type-specifier, the deduced type is determined using the rules for template argument deduction. If the deduction is for a return statement and the initializer is a braced-init-list ([dcl.init.list]), the program is ill-formed. Otherwise, obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list, with std::initializer_list<U>. Deduce a value for U using the rules of template argument deduction from a function call ([temp.deduct.call]), where P is a function template parameter type and the initializer is the corresponding argument. If the deduction fails, the declaration is ill-formed. Otherwise, the type deduced for the variable or return type is obtained by substituting the deduced U into P. [ Example:
auto x1 = { 1, 2 }; auto x2 = { 1, 2.0 };
— end example ]
[ Example:
const auto &i = expr;
The type of i is the deduced type of the parameter u in the call f(expr) of the following invented function template:
template <class U> void f(const U& u);
— end example ]
If the placeholder is the decltype(auto) type-specifier, the declared type of the variable or return type of the function shall be the placeholder alone. The type deduced for the variable or return type is determined as described in [dcl.type.simple], as though the initializer had been the operand of the decltype. [ Example:
int i; int&& f(); auto x3a = i; decltype(auto) x3d = i; auto x4a = (i); decltype(auto) x4d = (i); auto x5a = f(); decltype(auto) x5d = f(); auto x6a = { 1, 2 }; decltype(auto) x6d = { 1, 2 }; auto *x7a = &i; decltype(auto)*x7d = &i;
— end example ]
If the init-declarator-list contains more than one init-declarator, they shall all form declarations of variables. The type of each declared variable is determined as described above, and if the type that replaces the placeholder type is not the same in each deduction, the program is ill-formed.
[ Example:
auto x = 5, *y = &x; auto a = 5, b = { 1, 2 };
If a function with a declared return type that contains a placeholder type has multiple return statements, the return type is deduced for each return statement. If the type deduced is not the same in each deduction, the program is ill-formed.
If a function with a declared return type that uses a placeholder type has no return statements, the return type is deduced as though from a return statement with no operand at the closing brace of the function body. [ Example:
auto f() { } auto* g() { }
— end example ]
If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression, the program is ill-formed. Once a return statement has been seen in a function, however, the return type deduced from that statement can be used in the rest of the function, including in other return statements. [ Example:
auto n = n; auto f(); void g() { &f; } auto sum(int i) { if (i == 1) return i; else return sum(i-1)+i; }
— end example ]
Return type deduction for a function template with a placeholder in its declared type occurs when the definition is instantiated even if the function body contains a return statement with a non-type-dependent operand. [ Note: Therefore, any use of a specialization of the function template will cause an implicit instantiation. Any errors that arise from this instantiation are not in the immediate context of the function type and can result in the program being ill-formed. — end note ] [ Example:
template <class T> auto f(T t) { return t; } typedef decltype(f(1)) fint_t; template<class T> auto f(T* t) { return *t; } void g() { int (*p)(int*) = &f; }
— end example ]
Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type. [ Example:
auto f(); auto f() { return 42; } auto f(); int f(); decltype(auto) f(); template <typename T> auto g(T t) { return t; } template auto g(int); template char g(char); template<> auto g(double); template <class T> T g(T t) { return t; } template char g(char); template auto g(float); void h() { return g(42); } template <typename T> struct A { friend T frf(T); }; auto frf(int i) { return i; }
— end example ]
A function declared with a return type that uses a placeholder type shall not be virtual ([class.virtual]).
An explicit instantiation declaration ([temp.explicit]) does not cause the instantiation of an entity declared using a placeholder type, but it also does not prevent that entity from being instantiated as needed to determine its type. [ Example:
template <typename T> auto f(T t) { return t; } extern template auto f(int); int (*p)(int) = f;
— end example ]
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4