{ expression }indicates an optional expression enclosed in braces.
a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 _ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " '
hex-quad: hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit
universal-character-name: \u hex-quad \U hex-quad hex-quad
preprocessing-token: header-name import-keyword module-keyword export-keyword identifier pp-number character-literal user-defined-character-literal string-literal user-defined-string-literal preprocessing-op-or-punc each non-white-space character that cannot be one of the above
encoding-prefix R raw-string
#define R "x" const char* s = R"y"; // ill-formed raw string, not "x" "y"— end example
Alternative | Primary | Alternative | Primary | Alternative | Primary |
<% | { | and | && | and_eq | &= |
%> | } | bitor | | | or_eq | |= |
<: | [ | or | || | xor_eq | ^= |
:> | ] | xor | ^ | not | ! |
%: | # | compl | ~ | not_eq | != |
%:%: | ## | bitand | & |
token: identifier keyword literal operator-or-punctuator
header-name: < h-char-sequence > " q-char-sequence "
h-char-sequence: h-char h-char-sequence h-char
h-char: any member of the source character set except new-line and >
q-char-sequence: q-char q-char-sequence q-char
q-char: any member of the source character set except new-line and "
pp-number: digit . digit pp-number digit pp-number identifier-nondigit pp-number ' digit pp-number ' nondigit pp-number e sign pp-number E sign pp-number p sign pp-number P sign pp-number .
identifier: identifier-nondigit identifier identifier-nondigit identifier digit
identifier-nondigit: nondigit universal-character-name
nondigit: one of a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z _
digit: one of 0 1 2 3 4 5 6 7 8 9
00A8 | 00AA | 00AD | 00AF | 00B2-00B5 |
00B7-00BA | 00BC-00BE | 00C0-00D6 | 00D8-00F6 | 00F8-00FF |
0100-167F | 1681-180D | 180F-1FFF | ||
200B-200D | 202A-202E | 203F-2040 | 2054 | 2060-206F |
2070-218F | 2460-24FF | 2776-2793 | 2C00-2DFF | 2E80-2FFF |
3004-3007 | 3021-302F | 3031-D7FF | ||
F900-FD3D | FD40-FDCF | FDF0-FE44 | FE47-FFFD | |
10000-1FFFD | 20000-2FFFD | 30000-3FFFD | 40000-4FFFD | 50000-5FFFD |
60000-6FFFD | 70000-7FFFD | 80000-8FFFD | 90000-9FFFD | A0000-AFFFD |
B0000-BFFFD | C0000-CFFFD | D0000-DFFFD | E0000-EFFFD |
0300-036F | 1DC0-1DFF | 20D0-20FF | FE20-FE2F |
keyword: any identifier listed in Table tab:lex.key import-keyword module-keyword export-keyword
preprocessing-op-or-punc: preprocessing-operator operator-or-punctuator
preprocessing-operator: one of # ## %: %:%:
operator-or-punctuator: one of { } [ ] ( ) <: :> <% %> ; : ... ? :: . .* -> ->* ~ ! + - * / % ^ & | = += -= *= /= %= ^= &= |= == != < > <= >= <=> && || << >> <<= >>= ++ -- , and or xor not bitand bitor compl and_eq or_eq xor_eq not_eq
integer-literal: binary-literal integer-suffix octal-literal integer-suffix decimal-literal integer-suffix hexadecimal-literal integer-suffix
binary-literal: 0b binary-digit 0B binary-digit binary-literal ' binary-digit
octal-literal: 0 octal-literal ' octal-digit
decimal-literal: nonzero-digit decimal-literal ' digit
hexadecimal-literal: hexadecimal-prefix hexadecimal-digit-sequence
binary-digit: one of 0 1
octal-digit: one of 0 1 2 3 4 5 6 7
nonzero-digit: one of 1 2 3 4 5 6 7 8 9
hexadecimal-prefix: one of 0x 0X
hexadecimal-digit-sequence: hexadecimal-digit hexadecimal-digit-sequence ' hexadecimal-digit
hexadecimal-digit: one of 0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F
integer-suffix: unsigned-suffix long-suffix unsigned-suffix long-long-suffix long-suffix unsigned-suffix long-long-suffix unsigned-suffix
unsigned-suffix: one of u U
long-suffix: one of l L
long-long-suffix: one of ll LL
Kind of integer-literal | base N |
binary-literal | 2 |
octal-literal | 8 |
decimal-literal | 10 |
hexadecimal-literal | 16 |
integer-suffix | decimal-literal | integer-literal other than decimal-literal |
none | int | int |
long int | unsigned int | |
long long int | long int | |
unsigned long int | ||
long long int | ||
unsigned long long int | ||
u or U | unsigned int | unsigned int |
unsigned long int | unsigned long int | |
unsigned long long int | unsigned long long int | |
l or L | long int | long int |
long long int | unsigned long int | |
long long int | ||
unsigned long long int | ||
Both u or U | unsigned long int | unsigned long int |
and l or L | unsigned long long int | unsigned long long int |
ll or LL | long long int | long long int |
unsigned long long int | ||
Both u or U | unsigned long long int | unsigned long long int |
and ll or LL |
character-literal: encoding-prefix ' c-char-sequence '
encoding-prefix: one of u8 u U L
c-char-sequence: c-char c-char-sequence c-char
c-char: any member of the basic source character set except the single-quote ', backslash \, or new-line character escape-sequence universal-character-name
escape-sequence: simple-escape-sequence octal-escape-sequence hexadecimal-escape-sequence
simple-escape-sequence: one of \' \" \? \\ \a \b \f \n \r \t \v
octal-escape-sequence: \ octal-digit \ octal-digit octal-digit \ octal-digit octal-digit octal-digit
hexadecimal-escape-sequence: \x hexadecimal-digit hexadecimal-escape-sequence hexadecimal-digit
new-line | NL(LF) | \n |
horizontal tab | HT | \t |
vertical tab | VT | \v |
backspace | BS | \b |
carriage return | CR | \r |
form feed | FF | \f |
alert | BEL | \a |
backslash | \ | \\ |
question mark | ? | \? |
single quote | ' | \' |
double quote | " | \" |
octal number | ooo | \ooo |
hex number | hhh | \xhhh |
floating-point-literal: decimal-floating-point-literal hexadecimal-floating-point-literal
decimal-floating-point-literal: fractional-constant exponent-part floating-point-suffix digit-sequence exponent-part floating-point-suffix
hexadecimal-floating-point-literal: hexadecimal-prefix hexadecimal-fractional-constant binary-exponent-part floating-point-suffix hexadecimal-prefix hexadecimal-digit-sequence binary-exponent-part floating-point-suffix
fractional-constant: digit-sequence . digit-sequence digit-sequence .
hexadecimal-fractional-constant: hexadecimal-digit-sequence . hexadecimal-digit-sequence hexadecimal-digit-sequence .
exponent-part: e sign digit-sequence E sign digit-sequence
binary-exponent-part: p sign digit-sequence P sign digit-sequence
sign: one of + -
digit-sequence: digit digit-sequence ' digit
floating-point-suffix: one of f l F L
floating-point-suffix | type |
none | |
f or F | |
l or L |
string-literal: encoding-prefix " s-char-sequence " encoding-prefix R raw-string
s-char-sequence: s-char s-char-sequence s-char
s-char: any member of the basic source character set except the double-quote ", backslash \, or new-line character escape-sequence universal-character-name
raw-string: " d-char-sequence ( r-char-sequence ) d-char-sequence "
r-char-sequence: r-char r-char-sequence r-char
r-char: any member of the source character set, except a right parenthesis ) followed by the initial d-char-sequence (which may be empty) followed by a double quote ".
d-char-sequence: d-char d-char-sequence d-char
d-char: any member of the basic source character set except: space, the left parenthesis (, the right parenthesis ), the backslash \, and the control characters representing horizontal tab, vertical tab, form feed, and newline.
const char* p = R"(a\ b c)"; assert(std::strcmp(p, "a\\\nb\nc") == 0);
Source | Means | Source | Means | Source | Means | |||
u"a" | u"b" | u"ab" | U"a" | U"b" | U"ab" | L"a" | L"b" | L"ab" |
u"a" | "b" | u"ab" | U"a" | "b" | U"ab" | L"a" | "b" | L"ab" |
"a" | u"b" | u"ab" | "a" | U"b" | U"ab" | "a" | L"b" | L"ab" |
pointer-literal: nullptr
user-defined-literal: user-defined-integer-literal user-defined-floating-point-literal user-defined-string-literal user-defined-character-literal
user-defined-integer-literal: decimal-literal ud-suffix octal-literal ud-suffix hexadecimal-literal ud-suffix binary-literal ud-suffix
user-defined-floating-point-literal: fractional-constant exponent-part ud-suffix digit-sequence exponent-part ud-suffix hexadecimal-prefix hexadecimal-fractional-constant binary-exponent-part ud-suffix hexadecimal-prefix hexadecimal-digit-sequence binary-exponent-part ud-suffix
user-defined-string-literal: string-literal ud-suffix
user-defined-character-literal: character-literal ud-suffix
ud-suffix: identifier
operator "" X(nULL)
operator "" X("n")
operator "" X<'', '', ... ''>()where n is the source character sequence .
operator "" X(fL)
operator "" X("f")
operator "" X<'', '', ... ''>()where f is the source character sequence .
operator "" X<str>()
operator "" X(str, len)
operator "" X(ch)
long double operator "" _w(long double); std::string operator "" _w(const char16_t*, std::size_t); unsigned operator "" _w(const char*); int main() { 1.2_w; // calls operator "" _w(1.2L) u"one"_w; // calls operator "" _w(u"one", 3) 12_w; // calls operator "" _w("12") "two"_w; // error: no applicable literal operator }— end example
int a; // defines a extern const int c = 1; // defines c int f(int x) { return x+a; } // defines f and defines x struct S { int a; int b; }; // defines S, S::a, and S::b struct X { // defines X int x; // defines non-static data member x static int y; // declares static data member y X(): x(0) { } // defines a constructor of X }; int X::y = 1; // defines X::y enum { up, down }; // defines up and down namespace N { int d; } // defines N and N::d namespace N1 = N; // defines N1 X anX; // defines anXwhereas these are just declarations:
extern int a; // declares a extern const int c; // declares c int f(int); // declares f struct S; // declares S typedef int Int; // declares Int extern X anotherX; // declares anotherX using N::d; // declares d
#include <string> struct C { std::string s; // std::string is the standard library class ([string.classes]) }; int main() { C a; C b = a; b = a; }the implementation will implicitly define functions to make the definition of C equivalent to
struct C { std::string s; C() : s() { } C(const C& x): s(x.s) { } C(C&& x): s(static_cast<std::string&&>(x.s)) { } // : s(std::move(x.s)) { } C& operator=(const C& x) { s = x.s; return *this; } C& operator=(C&& x) { s = static_cast<std::string&&>(x.s); return *this; } // { s = std::move(x.s); return *this; } ~C() { } };
struct S { static const int x = 0; }; const int &f(const int &r); int n = b ? (1, S::x) // S::x is not odr-used here : f(S::x); // S::x is odr-used here, so a definition is required— end example
void f(int n) { [] { n = 1; }; // error: n is not odr-usable due to intervening lambda-expression struct A { void f() { n = 2; } // error: n is not odr-usable due to intervening function definition scope }; void g(int = n); // error: n is not odr-usable due to intervening function parameter scope [=](int k = n) {}; // error: n is not odr-usable due to being // outside the block scope of the lambda-expression [&] { [n]{ return n; }; }; // OK }— end example
auto f() { struct A {}; return A{}; } decltype(f()) g(); auto x = g();
struct X; // declare X as a struct type struct X* x1; // use X in pointer formation X* x2; // use X in pointer formation
// translation unit 1: struct X { X(int, int); X(int, int, int); }; X::X(int, int = 0) { } class D { X x = 0; }; D d1; // X(int, int) called by D() // translation unit 2: struct X { X(int, int); X(int, int, int); }; X::X(int, int = 0, int = 0) { } class D { X x = 0; }; D d2; // X(int, int, int) called by D(); // D()'s implicit definition violates the ODR— end example
inline void f(bool cond, void (*p)()) { if (cond) f(false, []{}); } inline void g(bool cond, void (*p)() = []{}) { if (cond) g(false); } struct X { void h(bool cond, void (*p)() = []{}) { if (cond) h(false); } };
int j = 24; int main() { int i = j, j; j = 42; }the identifier j is declared twice as a name (and used twice).
unsigned char x = 12; { unsigned char x = x; }
class-key attribute-specifier-seq identifier ;the identifier is declared to be a class-name in the scope that contains the declaration, otherwise
class-key identifierif the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the smallest namespace or block scope that contains the declaration.
typedef unsigned char T; template<class T = T // lookup finds the typedef name of unsigned char , T // lookup finds the template parameter N = 0> struct A { };— end example
if (int x = f()) { int x; // error: redeclaration of x } else { int x; // error: redeclaration of x }— end example
namespace N { int i; int g(int a) { return a; } int j(); void q(); } namespace { int l=1; } // the potential scope of l is from its point of declaration to the end of the translation unit namespace N { int g(char a) { // overloads N::g(int) return l+a; // l is from unnamed namespace } int i; // error: duplicate definition int j(); // OK: duplicate function declaration int j() { // OK: definition of N::j() return g(i); // calls N::g(int) } int q(); // error: different return type }— end example
Translation unit #1:
export module Q; export int sq(int i) { return i*i; }
Translation unit #2:
export module R; export import Q;
Translation unit #3:
import R; int main() { return sq(9); } // OK: sq from module Q— end example
typedef int c; enum { i = 1 }; class X { char v[i]; // error: i refers to ::i but when reevaluated is X::i int f() { return sizeof(c); } // OK: X::c char c; enum { i = 2 }; }; typedef char* T; struct Y { T a; // error: T refers to ::T but when reevaluated is Y::T typedef long T; T b; }; typedef int I; class D { typedef I I; // error, even though no reordering involved };— end example
namespace N { template<class T> struct A { }; // #1 template<class U> void f(U) { } // #2 struct B { template<class V> friend int g(struct C*); // #3 }; }
template<class T, T* p, class U = T> class X { /* ... */ }; template<class T> void f(T* p = new T);
template<class T> class X : public Array<T> { /* ... */ }; template<class T> class Y : public T { /* ... */ };
int h; void g(); namespace N { struct A {}; template <class T> int f(T); template <class T> int g(T); template <class T> int h(T); } int x = f<N::A>(N::A()); // OK: lookup of f finds nothing, f treated as template name int y = g<N::A>(N::A()); // OK: lookup of g finds a function, g treated as template name int z = h<N::A>(N::A()); // error: h< does not begin a template-id
typedef int f; namespace N { struct A { friend void f(A &); operator int(); void g(A a) { int i = f(a); // f is the typedef, not the friend function: equivalent to int(a) } }; }
namespace A { namespace N { void f(); } } void A::N::f() { i = 5; // The following scopes are searched for a declaration of i: // 1) outermost block scope of A::N::f, before the use of i // 2) scope of namespace N // 3) scope of namespace A // 4) global scope, before the definition of A::N::f }— end example
namespace M { class B { }; }
namespace N { class Y : public M::B { class X { int a[i]; }; }; } // The following scopes are searched for a declaration of i: // 1) scope of class N::Y::X, before the use of i // 2) scope of class N::Y, before the definition of N::Y::X // 3) scope of N::Y's base class M::B // 4) scope of namespace N, before the definition of N::Y // 5) global scope, before the definition of N— end example
class B { }; namespace M { namespace N { class X : public B { void f(); }; } } void M::N::X::f() { i = 16; } // The following scopes are searched for a declaration of i: // 1) outermost block scope of M::N::X::f, before the use of i // 2) scope of class M::N::X // 3) scope of M::N::X's base class B // 4) scope of namespace M::N // 5) scope of namespace M // 6) global scope, before the definition of M::N::X::f— end example
struct A { typedef int AT; void f1(AT); void f2(float); template <class T> void f3(); }; struct B { typedef char AT; typedef float BT; friend void A::f1(AT); // parameter type is A::AT friend void A::f2(BT); // parameter type is B::BT friend void A::f3<AT>(); // template argument is B::AT };— end example
namespace N { int i = 4; extern int j; } int i = 2; int N::j = i; // N::j == 4— end example
namespace N { struct S { }; void f(S); } void g() { N::S s; f(s); // OK: calls N::f (f)(s); // error: N::f not considered; parentheses prevent argument-dependent lookup }— end example
namespace NS { class T { }; void f(T); void g(T, int); } NS::T parm; void g(NS::T, float); int main() { f(parm); // OK: calls NS::f extern void g(NS::T, float); g(parm, 1); // OK: calls g(NS::T, float) }— end example
Translation unit #1:
export module M; namespace R { export struct X {}; export void f(X); } namespace S { export void f(R::X, R::X); }
Translation unit #2:
export module N; import M; export R::X make(); namespace R { static int g(X); } export template<typename T, typename U> void apply(T t, U u) { f(t, u); g(t); }
Translation unit #3:
module Q; import N; namespace S { struct Z { template<typename T> operator T(); }; } void test() { auto x = make(); // OK, decltype(x) is R::X in module M R::f(x); // error: R and R::f are not visible here f(x); // OK, calls R::f from interface of M f(x, S::Z()); // error: S::f in module M not considered // even though S is an associated namespace apply(x, S::Z()); // error: S::f is visible in instantiation context, but // R::g has internal linkage and cannot be used outside TU #2 }— end example
class A { public: static int n; }; int main() { int A; A::n = 42; // OK A b; // error: A does not name a type }— end example
class X { }; class C { class X { }; static const int number = 50; static X arr[number]; }; X C::arr[number]; // error: // equivalent to ::X C::arr[C::number]; // and not to C::X C::arr[C::number];— end example
nested-name-specifier type-name :: ~ type-namethe second type-name is looked up in the same scope as the first.
struct C { typedef int I; }; typedef int I1, I2; extern int* p; extern int* q; p->C::I::~I(); // I is looked up in the scope of C q->I1::~I2(); // I2 is looked up in the scope of the postfix-expression struct A { ~A(); }; typedef A AB; int main() { AB* p; p->AB::~AB(); // explicitly calls the destructor for A }— end example
struct A { A(); }; struct B: public A { B(); }; A::A() { } B::B() { } B::A ba; // object of type A A::A a; // error: A::A is not a type name struct A::A a2; // object of type A— end example
int x; namespace Y { void f(float); void h(int); } namespace Z { void h(double); } namespace A { using namespace Y; void f(int); void g(int); int i; } namespace B { using namespace Z; void f(char); int i; } namespace AB { using namespace A; using namespace B; void g(); } void h() { AB::g(); // g is declared directly in AB, therefore S is { AB::g() } and AB::g() is chosen AB::f(1); // f is not declared directly in AB so the rules are applied recursively to A and B; // namespace Y is not searched and Y::f(float) is not considered; // S is and overload resolution chooses A::f(int) AB::f('c'); // as above but resolution chooses B::f(char) AB::x++; // x is not declared directly in AB, and is not declared in A or B, so the rules // are applied recursively to Y and Z, S is { } so the program is ill-formed AB::i++; // i is not declared directly in AB so the rules are applied recursively to A and B, // S is so the use is ambiguous and the program is ill-formed AB::h(16.8); // h is not declared directly in AB and not declared directly in A or B so the rules // are applied recursively to Y and Z, S is and // overload resolution chooses Z::h(double) }— end example
namespace A { int a; } namespace B { using namespace A; } namespace C { using namespace A; } namespace BC { using namespace B; using namespace C; } void f() { BC::a++; // OK: S is } namespace D { using A::a; } namespace BD { using namespace B; using namespace D; } void g() { BD::a++; // OK: S is }— end example
namespace B { int b; } namespace A { using namespace B; int a; } namespace B { using namespace A; } void f() { A::a++; // OK: a declared directly in A, S is { A::a } B::a++; // OK: both A and B searched (once), S is { A::a } A::b++; // OK: both A and B searched (once), S is { B::b } B::b++; // OK: b declared directly in B, S is { B::b } }
namespace A { struct x { }; int x; int y; } namespace B { struct y { }; } namespace C { using namespace A; using namespace B; int i = C::x; // OK, A::x (of type int) int j = C::y; // ambiguous, A::y or B::y }— end example
nested-name-specifier unqualified-idthe unqualified-id shall name a member of the namespace designated by the nested-name-specifier or of an element of the inline namespace set of that namespace.
namespace A { namespace B { void f1(int); } using namespace B; } void A::f1(int){ } // error: f1 is not a member of A— end example
namespace A { namespace B { void f1(int); } } namespace C { namespace D { void f1(int); } } using namespace A; using namespace C::D; void B::f1(int){ } // OK, defines A::B::f1(int)— end example
class-key attribute-specifier-seq identifier ;the identifier is looked up according to [basic.lookup.unqual] but ignoring any non-type names that have been declared.
class-key attribute-specifier-seq identifier ;the elaborated-type-specifier is a declaration that introduces the class-name as described in [basic.scope.pdecl].
struct Node { struct Node* Next; // OK: Refers to injected-class-name Node struct Data* Data; // OK: Declares type Data at global scope and member Data }; struct Data { struct Node* Node; // OK: Refers to Node at global scope friend struct ::Glob; // error: Glob is not declared, cannot introduce a qualified type ([dcl.type.elab]) friend struct Glob; // OK: Refers to (as yet) undeclared Glob at global scope. /* ... */ }; struct Base { struct Data; // OK: Declares nested Data struct ::Data* thatData; // OK: Refers to ::Data struct Base::Data* thisData; // OK: Refers to nested Data friend class ::Data; // OK: global Data is a friend friend class Data; // OK: nested Data is a friend struct Data { /* ... */ }; // Defines nested Data }; struct Data; // OK: Redeclares Data at global scope struct ::Data; // error: cannot introduce a qualified type ([dcl.type.elab]) struct Base::Data; // error: cannot introduce a qualified type ([dcl.type.elab]) struct Base::Datum; // error: Datum undefined struct Base::Data* pBase; // OK: refers to nested Data— end example
struct A { }; struct B { struct A { }; void f(::A* a); }; void B::f(::A* a) { a->~A(); // OK: lookup in *a finds the injected-class-name }— end example
class-name-or-namespace-name::...the class-name-or-namespace-name following the . or -> operator is first looked up in the class of the object expression ([class.member.lookup]) and the name, if found, is used.
::class-name-or-namespace-name::...the class-name-or-namespace-name is looked up in global scope as a class-name or namespace-name.
struct A { }; namespace N { struct A { void g() { } template <class T> operator T(); }; } int main() { N::A a; a.operator A(); // calls N::A::operator N::A }— end example
translation-unit: declaration-seq global-module-fragment module-declaration declaration-seq private-module-fragment
static void f(); extern "C" void h(); static int i = 0; // #1 void g() { extern void f(); // internal linkage extern void h(); // C language linkage int i; // #2: i has no linkage { extern void f(); // internal linkage extern int i; // #3: external linkage, ill-formed } }
namespace X { void p() { q(); // error: q not yet declared extern void q(); // q is a member of namespace X } void middle() { q(); // error: q not yet declared } void q() { /* ... */ } // definition of X::q } void q() { /* ... */ } // some other, unrelated q— end example
"decls.h":
int f(); // #1, attached to the global module int g(); // #2, attached to the global module
Module interface of M:
module; #include "decls.h" export module M; export using ::f; // OK: does not declare an entity, exports #1 int g(); // error: matches #2, but attached to M export int h(); // #3 export int k(); // #4
Other translation unit:
import M; static int h(); // error: matches #3 int k(); // error: matches #4— end example
Translation unit #1:
export module A; static void f() {} inline void it() { f(); } // error: is an exposure of f static inline void its() { f(); } // OK template<int> void g() { its(); } // OK template void g<0>(); decltype(f) *fp; // error: f (though not its type) is TU-local auto &fr = f; // OK constexpr auto &fr2 = fr; // error: is an exposure of f constexpr static auto fp2 = fr; // OK struct S { void (&ref)(); } s{f}; // OK, value is TU-local constexpr extern struct W { S &s; } wrap{s}; // OK, value is not TU-local static auto x = []{f();}; // OK auto x2 = x; // error: the closure type is TU-local int y = ([]{f();}(),0); // error: the closure type is not TU-local int y2 = (x,0); // OK namespace N { struct A {}; void adl(A); static void adl(int); } void adl(double); inline void h(auto x) { adl(x); } // OK, but a specialization might be an exposure
Translation unit #2:
module A; void other() { g<0>(); // OK, specialization is explicitly instantiated g<1>(); // error: instantiation uses TU-local its h(N::A{}); // error: overload set contains TU-local N::adl(int) h(0); // OK, calls adl(double) adl(N::A{}); // OK; N::adl(int) not found, calls N::adl(N::A) fr(); // OK, calls f constexpr auto ptr = fr; // error: fr is not usable in constant expressions here }— end example
template<typename ...T> struct AlignedUnion { alignas(T...) unsigned char data[max(sizeof(T)...)]; }; int f() { AlignedUnion<int, char> au; int *p = new (au.data) int; // OK, au.data provides storage char *c = new (au.data) char(); // OK, ends lifetime of *p char *d = new (au.data + 1) char(); return *c + *d; // OK } struct A { unsigned char a[32]; }; struct B { unsigned char b[16]; }; A a; B *b = new (a.a + 8) B; // a.a provides storage for *b int *p = new (b->b + 4) int; // b->b provides storage for *p // a.a does not provide storage for *p (directly), // but *p is nested within a (see below)— end example
static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // always true— end example
#include <cstdlib> struct X { int a, b; }; X *make_x() { // The call to std::malloc implicitly creates an object of type X // and its subobjects a and b, and returns a pointer to that X object // (or an object that is pointer-interconvertible ([basic.compound]) with it), // in order to give the subsequent class member access operations // defined behavior. X *p = (X*)std::malloc(sizeof(struct X)); p->a = 1; p->b = 2; return p; }— end example
#include <cstdlib> struct B { virtual void f(); void mutate(); virtual ~B(); }; struct D1 : B { void f(); }; struct D2 : B { void f(); }; void B::mutate() { new (this) D2; // reuses storage --- ends the lifetime of *this f(); // undefined behavior ... = this; // OK, this points to valid memory } void g() { void* p = std::malloc(sizeof(D1) + sizeof(D2)); B* pb = new (p) D1; pb->mutate(); *pb; // OK: pb points to valid memory void* q = pb; // OK: pb points to valid memory pb->f(); // undefined behavior: lifetime of *pb has ended }— end example
struct C { int i; void f(); const C& operator=( const C& ); }; const C& C::operator=( const C& other) { if ( this != &other ) { this->~C(); // lifetime of *this ends new (this) C(other); // new object of type C created f(); // well-defined } return *this; } C c1; C c2; c1 = c2; // well-defined c1.f(); // well-defined; c1 refers to a new object of type C— end example
class T { }; struct B { ~B(); }; void h() { B b; new (&b) T; } // undefined behavior at block exit— end example
struct B { B(); ~B(); }; const B b; void h() { b.~B(); new (const_cast<B*>(&b)) const B; // undefined behavior }— end example
int f(bool b) { unsigned char c; unsigned char d = c; // OK, d has an indeterminate value int e = d; // undefined behavior return b ? d : 0; // undefined behavior if b is true }— end example
[[nodiscard]] void* operator new(std::size_t); [[nodiscard]] void* operator new(std::size_t, std::align_val_t); void operator delete(void*) noexcept; void operator delete(void*, std::size_t) noexcept; void operator delete(void*, std::align_val_t) noexcept; void operator delete(void*, std::size_t, std::align_val_t) noexcept; [[nodiscard]] void* operator new[](std::size_t); [[nodiscard]] void* operator new[](std::size_t, std::align_val_t); void operator delete[](void*) noexcept; void operator delete[](void*, std::size_t) noexcept; void operator delete[](void*, std::align_val_t) noexcept; void operator delete[](void*, std::size_t, std::align_val_t) noexcept;
struct B { long double d; }; struct D : virtual B { char c; };
class X { public: X(int); X(const X&); X& operator=(const X&); ~X(); }; class Y { public: Y(int); Y(Y&&); ~Y(); }; X f(X); Y g(Y); void h() { X a(1); X b = f(X(2)); Y c = g(Y(3)); a = f(a); }
template<typename T> using id = T; int i = 1; int&& a = id<int[3]>{1, 2, 3}[i]; // temporary array has same lifetime as a const int& b = static_cast<const int&>(0); // temporary int has same lifetime as b int&& c = cond ? id<int[3]>{1, 2, 3}[i] : static_cast<int&&>(0); // exactly one of the two temporaries is lifetime-extended— end example
const int& x = (const int&)1; // temporary for value 1 has same lifetime as x— end example
struct S { int mi; const std::pair<int,int>& mp; }; S a { 1, {2,3} }; S* p = new S{ 1, {2,3} }; // creates dangling reference— end example
struct S { S(); S(int); friend S operator+(const S&, const S&); ~S(); }; S obj1; const S& cr = S(16)+S(23); S obj2;
constexpr std::size_t N = sizeof(T); char buf[N]; T obj; // obj initialized to its original value std::memcpy(buf, &obj, N); // between these two calls to std::memcpy, obj might be modified std::memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type holds its original value— end example
T* t1p; T* t2p; // provided that t2p points to an initialized object ... std::memcpy(t1p, t2p, sizeof(T)); // at this point, every subobject of trivially copyable type in *t1p contains // the same value as the corresponding subobject in *t2p— end example
class X; // X is an incomplete type extern X* xp; // xp is a pointer to an incomplete type extern int arr[]; // the type of arr is incomplete typedef int UNKA[]; // UNKA is an incomplete type UNKA* arrp; // arrp is a pointer to an incomplete type UNKA** arrpp; void foo() { xp++; // error: X is incomplete arrp++; // error: incomplete type arrpp++; // OK: sizeof UNKA* is known } struct X { int i; }; // now X is a complete type int arr[10]; // now the type of arr is complete X x; void bar() { xp = &x; // OK; type is “pointer to X” arrp = &arr; // error: different types xp++; // OK: X is complete arrp++; // error: UNKA can't be completed }— end example
Type | Minimum width N |
signed char | 8 |
short | 16 |
int | 16 |
long | 32 |
long long | 64 |
no cv-qualifier | < | const |
no cv-qualifier | < | volatile |
no cv-qualifier | < | const volatile |
const | < | const volatile |
volatile | < | const volatile |
struct A { int x; }; struct B { int y; struct A a; }; B b = { 5, { 1+1 } };— end example
struct S { S(int i): I(i) { } // full-expression is initialization of I int& v() { return I; } ~S() noexcept(false) { } private: int I; }; S s1(1); // full-expression comprises call of S::S(int) void f() { S s2 = 2; // full-expression comprises call of S::S(int) if (S(3).v()) // full-expression includes lvalue-to-rvalue and int to bool conversions, // performed before temporary is deleted at end of full-expression { } bool b = noexcept(S()); // exception specification of destructor of S considered for noexcept // full-expression is destruction of s2 at end of block } struct B { B(S = S(0)); }; B b[2] = { B(), B() }; // full-expression is the entire initialization // including the destruction of temporaries— end example
void g(int i) { i = 7, i++, i++; // i becomes 9 i = i++ + 1; // the value of i is incremented i = i++ + i; // undefined behavior i = i + 1; // the value of i is incremented }— end example
inline double fd() { return 1.0; } extern double d1; double d2 = d1; // unspecified: // may be statically initialized to 0.0 or // dynamically initialized to 0.0 if d1 is // dynamically initialized, or 1.0 otherwise double d1 = fd(); // may be initialized statically or dynamically to 1.0
// - File 1 - #include "a.h" #include "b.h" B b; A::A(){ b.Use(); } // - File 2 - #include "a.h" A a; // - File 3 - #include "a.h" #include "b.h" extern A a; extern B b; int main() { a.Use(); b.Use(); }
int a, b; /* ... */ a = a + 32760 + b + 5;the expression statement behaves exactly the same as
a = (((a + 32760) + b) + 5);due to the associativity and precedence of these operators.
a = ((a + b) + 32765);since if the values for a and b were, respectively, -32754 and -15, the sum a + b would produce an exception while the original expression would not; nor can the expression be rewritten as either
a = ((a + 32765) + b);or
a = (a + (b + 32765));since the values for a and b might have been, respectively, 4 and -8 or -17 and 12.
struct S { int n; }; auto f() { S x { 1 }; constexpr S y { 2 }; return [&](bool b) { return (b ? y : x).n; }; } auto g = f(); int m = g(false); // undefined behavior: access of x.n outside its lifetime int n = g(true); // OK, does not access y.n— end example
struct X { int n; }; int k = X().n; // OK, X() prvalue is converted to xvalue— end example
int main() { const char c = 'c'; char* pc; const char** pcc = &pc; // #1: not allowed *pcc = &c; *pc = 'C'; // #2: modifies a const object }
void (*p)(); void (**pp)() noexcept = &p; // error: cannot convert to pointer to noexcept function struct S { typedef void (*p)(); operator p(); }; void (*q)() noexcept = S(); // error: cannot convert to pointer to noexcept function— end example
primary-expression: literal this ( expression ) id-expression lambda-expression fold-expression requires-expression
struct A { char g(); template<class T> auto f(T t) -> decltype(t + g()) { return t + g(); } }; template auto A::f(int t) -> decltype(t + g());— end example
class Outer { int a[sizeof(*this)]; // error: not inside a member function unsigned int sz = sizeof(*this); // OK: in default member initializer void f() { int b[sizeof(*this)]; // OK struct Inner { int c[sizeof(*this)]; // error: not inside a member function of Inner }; } };— end example
id-expression: unqualified-id qualified-id
struct S { int m; }; int i = sizeof(S::m); // OK int j = sizeof(S::m + 42); // OK— end example
template<typename T> struct A { static void f(int) requires false; } void g() { A<int>::f(0); // error: cannot call f void (*p1)(int) = A<int>::f; // error: cannot take the address of f decltype(A<int>::f)* p2 = nullptr; // error: the type decltype(A<int>::f) is invalid }
unqualified-id: identifier operator-function-id conversion-function-id literal-operator-id ~ type-name ~ decltype-specifier template-id
void f() { float x, &r = x; [=] { decltype(x) y1; // y1 has type float decltype((x)) y2 = y1; // y2 has type float const& because this lambda // is not mutable and x is an lvalue decltype(r) r1 = y1; // r1 has type float& decltype((r)) r2 = y2; // r2 has type float const& }; }— end example
qualified-id: nested-name-specifier template unqualified-id
nested-name-specifier: :: type-name :: namespace-name :: decltype-specifier :: nested-name-specifier identifier :: nested-name-specifier template simple-template-id ::
struct C { }; void f() { C * pc = new C; using C2 = C; pc->C::~C2(); // OK, destroys *pc C().C::~C(); // undefined behavior: temporary of type C destroyed twice using T = int; 0 .T::~T(); // OK, no effect 0.T::~T(); // error: 0.T is a user-defined-floating-point-literal ([lex.ext]) }— end example
lambda-expression: lambda-introducer lambda-declarator compound-statement lambda-introducer < template-parameter-list > requires-clause lambda-declarator compound-statement
lambda-introducer: [ lambda-capture ]
lambda-declarator: ( parameter-declaration-clause ) decl-specifier-seq noexcept-specifier attribute-specifier-seq trailing-return-type requires-clause
#include <algorithm> #include <cmath> void abssort(float* x, unsigned N) { std::sort(x, x + N, [](float a, float b) { return std::abs(a) < std::abs(b); }); }— end example
auto x1 = [](int i){ return i; }; // OK: return type is int auto x2 = []{ return { 1, 2 }; }; // error: deducing return type from braced-init-list int j; auto x3 = []()->auto&& { return j; }; // OK: return type is int&— end example
int i = [](int i, auto a) { return i; }(3, 4); // OK: a generic lambda int j = []<class T>(T t, int i) { return i; }(3, 4); // OK: a generic lambda— end example
auto glambda = [](auto a, auto&& b) { return a < b; }; bool b = glambda(3, 3.14); // OK auto vglambda = [](auto printer) { return [=](auto&& ... ts) { // OK: ts is a function parameter pack printer(std::forward<decltype(ts)>(ts)...); return [=]() { printer(ts ...); }; }; }; auto p = vglambda( [](auto v1, auto v2, auto v3) { std::cout << v1 << v2 << v3; } ); auto q = p(1, 'a', 3.14); // OK: outputs 1a3.14 q(); // OK: outputs 1a3.14— end example
auto ID = [](auto a) { return a; }; static_assert(ID(3) == 3); // OK struct NonLiteral { NonLiteral(int n) : n(n) { } int n; }; static_assert(ID(NonLiteral{3}).n == 3); // error— end example
auto monoid = [](auto v) { return [=] { return v; }; }; auto add = [](auto m1) constexpr { auto ret = m1(); return [=](auto m2) mutable { auto m1val = m1(); auto plus = [=](auto m2val) mutable constexpr { return m1val += m2val; }; ret = plus(m2()); return monoid(ret); }; }; constexpr auto zero = monoid(0); constexpr auto one = monoid(1); static_assert(add(one)(zero)() == one()); // OK // Since two below is not declared constexpr, an evaluation of its constexpr member function call operator // cannot perform an lvalue-to-rvalue conversion on one of its subobjects (that represents its capture) // in a constant expression. auto two = monoid(2); assert(two() == 2); // OK, not a constant expression. static_assert(add(one)(one)() == two()); // error: two() is not a constant expression static_assert(add(one)(one)() == monoid(2)()); // OK— end example
template <typename T> concept C1 = /* ... */; template <std::size_t N> concept C2 = /* ... */; template <typename A, typename B> concept C3 = /* ... */; auto f = []<typename T1, C1 T2> requires C2<sizeof(T1) + sizeof(T2)> (T1 a1, T1 b1, T2 a2, auto a3, auto a4) requires C3<decltype(a4), T2> { // T2 is constrained by a type-constraint. // T1 and T2 are constrained by a requires-clause, and // T2 and the type of a4 are constrained by a trailing requires-clause. };— end example
auto glambda = [](auto a) { return a; }; int (*fp)(int) = glambda;
struct Closure { template<class T> auto operator()(T t) const { /* ... */ } template<class T> static auto lambda_call_operator_invoker(T a) { // forwards execution to operator()(a) and therefore has // the same return type deduced /* ... */ } template<class T> using fptr_t = decltype(lambda_call_operator_invoker(declval<T>())) (*)(T); template<class T> operator fptr_t<T>() const { return &lambda_call_operator_invoker; } };
void f1(int (*)(int)) { } void f2(char (*)(int)) { } void g(int (*)(int)) { } // #1 void g(char (*)(char)) { } // #2 void h(int (*)(int)) { } // #3 void h(char (*)(int)) { } // #4 auto glambda = [](auto a) { return a; }; f1(glambda); // OK f2(glambda); // error: ID is not convertible g(glambda); // error: ambiguous h(glambda); // OK: calls #3 since it is convertible from ID int& (*fpi)(int*) = [](auto* a) -> auto& { return *a; }; // OK— end example
auto GL = [](auto a) { std::cout << a; return a; }; int (*GL_int)(int) = GL; // OK: through conversion function template GL_int(3); // OK: same as GL(3)— end example
auto Fwd = [](int (*fp)(int), auto a) { return fp(a); }; auto C = [](auto a) { return a; }; static_assert(Fwd(C,3) == 3); // OK // No specialization of the function call operator template can be constexpr (due to the local static). auto NC = [](auto a) { static int s; return a; }; static_assert(Fwd(NC,3) == 3); // error— end example
struct S1 { int x, y; int operator()(int); void f() { [=]()->int { return operator()(this->x + y); // equivalent to S1::operator()(this->x + (*this).y) // this has type S1* }; } };— end example
lambda-capture: capture-default capture-list capture-default , capture-list
capture-default: & =
capture-list: capture capture-list , capture
capture: simple-capture init-capture
simple-capture: identifier ... & identifier ... this * this
init-capture: ... identifier initializer & ... identifier initializer
struct S2 { void f(int i); }; void S2::f(int i) { [&, i]{ }; // OK [&, this, i]{ }; // OK, equivalent to [&, i] [&, &i]{ }; // error: i preceded by & when & is the default [=, *this]{ }; // OK [=, this]{ }; // OK, equivalent to [=] [i, i]{ }; // error: i repeated [this, *this]{ }; // error: this appears twice }— end example
void f() { int x = 0; auto g = [x](int x) { return 0; }; // error: parameter and simple-capture have the same name }— end example
int x = 4; auto y = [&r = x, x = x+1]()->int { r += 2; return x+2; }(); // Updates ::x to 6, and initializes y to 7. auto z = [a = 42](int a) { return 1; }; // error: parameter and local variable have the same name— end example
void f(int, const int (&)[2] = {}); // #1 void f(const int&, const int (&)[1]); // #2 void test() { const int x = 17; auto g = [](auto a) { f(x); // OK: calls #1, does not capture x }; auto g1 = [=](auto a) { f(x); // OK: calls #1, captures x }; auto g2 = [=](auto a) { int selector[sizeof(a) == 1 ? 1 : 2]{}; f(x, selector); // OK: captures x, might call #1 or #2 }; auto g3 = [=](auto a) { typeid(a + x); // captures x regardless of whether a + x is an unevaluated operand }; }— end example
template<bool B> void f(int n) { [=](auto a) { if constexpr (B && sizeof(a) > 4) { (void)n; // captures n regardless of the value of B and sizeof(int) } }(0); }— end example
void f1(int i) { int const N = 20; auto m1 = [=]{ int const M = 30; auto m2 = [i]{ int x[N][M]; // OK: N and M are not odr-used x[0][0] = i; // OK: i is explicitly captured by m2 and implicitly captured by m1 }; }; struct s1 { int f; void work(int n) { int m = n*n; int j = 40; auto m3 = [this,m] { auto m4 = [&,j] { // error: j not odr-usable due to intervening lambda m3 int x = n; // error: n is odr-used but not odr-usable due to intervening lambda m3 x += m; // OK: m implicitly captured by m4 and explicitly captured by m3 x += i; // error: i is odr-used but not odr-usable // due to intervening function and class scopes x += f; // OK: this captured implicitly by m4 and explicitly by m3 }; }; } }; } struct s2 { double ohseven = .007; auto f() { return [this] { return [*this] { return ohseven; // OK }; }(); } auto g() { return [] { return [*this] { }; // error: *this not captured by outer lambda-expression }(); } };— end example
void f2() { int i = 1; void g1(int = ([i]{ return i; })()); // error void g2(int = ([i]{ return 0; })()); // error void g3(int = ([=]{ return i; })()); // error void g4(int = ([=]{ return 0; })()); // OK void g5(int = ([]{ return sizeof i; })()); // OK void g6(int = ([x=1]{ return x; })()); // OK void g7(int = ([x=i]{ return x; })()); // error }— end example
void f(const int*); void g() { const int N = 10; [=] { int arr[N]; // OK: not an odr-use, refers to automatic variable f(&N); // OK: causes N to be captured; &N points to // the corresponding member of the closure type }; }— end example
// The inner closure type must be a literal type regardless of how reference captures are represented. static_assert([](int n) { return [&n] { return ++n; }(); }(3) == 4);— end example
auto h(int &r) { return [&] { ++r; // Valid after h returns if the lifetime of the // object to which r is bound has not ended }; }— end example
int a = 1, b = 1, c = 1; auto m1 = [a, &b, &c]() mutable { auto m2 = [a, b, &c]() mutable { std::cout << a << b << c; a = 4; b = 4; c = 4; }; a = 3; b = 3; c = 3; m2(); }; a = 2; b = 2; c = 2; m1(); std::cout << a << b << c;— end example
template<class... Args> void f(Args... args) { auto lm = [&, args...] { return g(args...); }; lm(); auto lm2 = [...xs=std::move(args)] { return g(xs...); }; lm2(); }— end example
fold-expression: ( cast-expression fold-operator ... ) ( ... fold-operator cast-expression ) ( cast-expression fold-operator ... fold-operator cast-expression )
fold-operator: one of + - * / % ^ & | << >> += -= *= /= %= ^= &= |= <<= >>= = == != < > <= >= && || , .* ->*
template<typename ...Args> bool f(Args ...args) { return (true && ... && args); // OK } template<typename ...Args> bool f(Args ...args) { return (args + ... + args); // error: both operands contain unexpanded packs }— end example
requires-expression: requires requirement-parameter-list requirement-body
requirement-parameter-list: ( parameter-declaration-clause )
requirement-body: { requirement-seq }
requirement-seq: requirement requirement-seq requirement
requirement: simple-requirement type-requirement compound-requirement nested-requirement
template<typename T> concept R = requires (T i) { typename T::type; {*i} -> std::convertible_to<const typename T::type&>; };
template<typename T> requires requires (T x) { x + x; } T add(T a, T b) { return a + b; }
template<typename T> concept C = requires(T t, ...) { // error: terminates with an ellipsis t; };— end example
template<typename T> concept C = requires { new int[-(int)sizeof(T)]; // ill-formed, no diagnostic required };— end example
simple-requirement: expression ;
template<typename T> concept C = requires (T a, T b) { a + b; // C<T> is true if a + b is a valid expression };— end example
type-requirement: typename nested-name-specifier type-name ;
template<typename T, typename T::type = 0> struct S; template<typename T> using Ref = T&; template<typename T> concept C = requires { typename T::inner; // required nested member name typename S<T>; // required class template specialization typename Ref<T>; // required alias template substitution, fails if T is void };— end example
compound-requirement: { expression } noexcept return-type-requirement ;
return-type-requirement: -> type-constraint
template<typename T> concept C1 = requires(T x) { {x++}; };
template<typename T> concept C2 = requires(T x) { {*x} -> std::same_as<typename T::inner>; };
template<typename T> concept C3 = requires(T x) { {g(x)} noexcept; };
nested-requirement: requires constraint-expression ;
template<typename U> concept C = sizeof(U) == 1; template<typename T> concept D = requires (T t) { requires C<decltype (+t)>; };D<T> is satisfied if sizeof(decltype (+t)) == 1 ([temp.constr.atomic]).
template<typename T> concept C = requires (T a) { requires sizeof(a) == 4; // OK requires a == 0; // error: evaluation of a constraint variable };— end example
postfix-expression: primary-expression postfix-expression [ expr-or-braced-init-list ] postfix-expression ( expression-list ) simple-type-specifier ( expression-list ) typename-specifier ( expression-list ) simple-type-specifier braced-init-list typename-specifier braced-init-list postfix-expression . template id-expression postfix-expression -> template id-expression postfix-expression ++ postfix-expression -- dynamic_cast < type-id > ( expression ) static_cast < type-id > ( expression ) reinterpret_cast < type-id > ( expression ) const_cast < type-id > ( expression ) typeid ( expression ) typeid ( type-id )
expression-list: initializer-list
template<typename ...T> int f(int n = 0, T ...t); int x = f<int>(); // error: no argument for second function parameter— end example
void f() { std::string s = "but I have heard it works even if you don't believe in it"; s.replace(0, 4, "").replace(s.find("even"), 4, "only").replace(s.find(" don't"), 6, ""); assert(s == "I have heard it works only if you believe in it"); // OK }— end example
struct S { S(int); }; int operator<<(S, int); int i, j; int x = S(i=1) << (i=2); int y = operator<<(S(j=1), j=2);
struct B { }; struct D : B { }; void foo(D* dp) { B* bp = dynamic_cast<B*>(dp); // equivalent to B* bp = dp; }— end example
class A { virtual void f(); }; class B { virtual void g(); }; class D : public virtual A, private B { }; void g() { D d; B* bp = (B*)&d; // cast needed to break protection A* ap = &d; // public derivation, no cast needed D& dr = dynamic_cast<D&>(*bp); // fails ap = dynamic_cast<A*>(bp); // fails bp = dynamic_cast<B*>(ap); // fails ap = dynamic_cast<A*>(&d); // succeeds bp = dynamic_cast<B*>(&d); // ill-formed (not a runtime check) } class E : public D, public B { }; class F : public E, public D { }; void h() { F f; A* ap = &f; // succeeds: finds unique A D* dp = dynamic_cast<D*>(ap); // fails: yields null; f has two D subobjects E* ep = (E*)ap; // error: cast from virtual base E* ep1 = dynamic_cast<E*>(ap); // succeeds }— end example
class D { /* ... */ }; D d1; const D d2; typeid(d1) == typeid(d2); // yields true typeid(D) == typeid(const D); // yields true typeid(D) == typeid(d2); // yields true typeid(D) == typeid(const D&); // yields true— end example
struct B { }; struct D : public B { }; D d; B &br = d; static_cast<D&>(br); // produces lvalue denoting the original d object— end example
T t(E);for some invented temporary variable t ([dcl.init]) and then using the temporary variable as the result of the conversion.
struct B { }; struct D : private B { }; void f() { static_cast<D*>((B*)0); // error: B is a private base of D static_cast<int B::*>((int D::*)0); // error: B is a private base of D }— end example
T* p1 = new T; const T* p2 = static_cast<const T*>(static_cast<void*>(p1)); bool b = p1 == p2; // b will have the value true.— end example
typedef int *A[3]; // array of 3 pointer to int typedef const int *const CA[3]; // array of 3 const pointer to const int CA &&r = A{}; // OK, reference binds to temporary array object // after qualification conversion to type CA A &&r1 = const_cast<A>(CA{}); // error: temporary array decayed to pointer A &&r2 = const_cast<A&&>(CA{}); // OK— end example
unary-expression: postfix-expression unary-operator cast-expression ++ cast-expression -- cast-expression await-expression sizeof unary-expression sizeof ( type-id ) sizeof ... ( identifier ) alignof ( type-id ) noexcept-expression new-expression delete-expression
unary-operator: one of * & + - ! ~
struct A { int i; }; struct B : A { }; ... &B::i ... // has type int A::* int a; int* p1 = &a; int* p2 = p1 + 1; // defined behavior bool b = p2 > p1; // defined behavior, with value true— end example
await-expression: co_await cast-expression
template <typename T> struct my_future { /* ... */ bool await_ready(); void await_suspend(std::coroutine_handle<>); T await_resume(); }; template <class Rep, class Period> auto operator co_await(std::chrono::duration<Rep, Period> d) { struct awaiter { std::chrono::system_clock::duration duration; /* ... */ awaiter(std::chrono::system_clock::duration d) : duration(d) {} bool await_ready() const { return duration.count() <= 0; } void await_resume() {} void await_suspend(std::coroutine_handle<> h) { /* ... */ } }; return awaiter{d}; } using namespace std::chrono; my_future<int> h(); my_future<void> g() { std::cout << "just about go to sleep...\n"; co_await 10ms; std::cout << "resumed\n"; co_await h(); } auto f(int x = co_await h()); // error: await-expression outside of function suspension context int a[] = { co_await h() }; // error: await-expression outside of function suspension context— end example
template<class... Types> struct count { static const std::size_t value = sizeof...(Types); };— end example
noexcept-expression: noexcept ( expression )
new-expression: :: new new-placement new-type-id new-initializer :: new new-placement ( type-id ) new-initializer
new-placement: ( expression-list )
new-type-id: type-specifier-seq new-declarator
new-declarator: ptr-operator new-declarator noptr-new-declarator
noptr-new-declarator: [ expression ] attribute-specifier-seq noptr-new-declarator [ constant-expression ] attribute-specifier-seq
new-initializer: ( expression-list ) braced-init-list
T x init ;
new auto(1); // allocated type is int auto x = new auto('a'); // allocated type is char, x is of type char* template<class T> struct A { A(T, T); }; auto y = new A{1, 2}; // allocated type is A<int>— end example
new int(*[10])(); // erroris ill-formed because the binding is
(new int) (*[10])(); // error
new (int (*[10])());allocates an array of 10 pointers to functions (taking no argument and returning int).
void can_merge(int x) { // These allocations are safe for merging: std::unique_ptr<char[]> a{new (std::nothrow) char[8]}; std::unique_ptr<char[]> b{new (std::nothrow) char[8]}; std::unique_ptr<char[]> c{new (std::nothrow) char[x]}; g(a.get(), b.get(), c.get()); } void cannot_merge(int x) { std::unique_ptr<char[]> a{new char[8]}; try { // Merging this allocation would change its catch handler. std::unique_ptr<char[]> b{new char[x]}; } catch (const std::bad_alloc& e) { std::cerr << "Allocation failed: " << e.what() << std::endl; throw; } }— end example
operator new(sizeof(T)) operator new(sizeof(T), std::align_val_t(alignof(T)))
operator new(sizeof(T), 2, f) operator new(sizeof(T), std::align_val_t(alignof(T)), 2, f)
operator new[](sizeof(T) * 5 + x) operator new[](sizeof(T) * 5 + x, std::align_val_t(alignof(T)))
operator new[](sizeof(T) * 5 + x, 2, f) operator new[](sizeof(T) * 5 + x, std::align_val_t(alignof(T)), 2, f)
struct S { // Placement allocation function: static void* operator new(std::size_t, std::size_t); // Usual (non-placement) deallocation function: static void operator delete(void*, std::size_t); }; S* p = new (0) S; // error: non-placement deallocation function matches // placement allocation function— end example
delete-expression: :: delete cast-expression :: delete [ ] cast-expression
cast-expression: unary-expression ( type-id ) cast-expression
struct A { }; struct I1 : A { }; struct I2 : A { }; struct D : I1, I2 { }; A* foo( D* p ) { return (A*)( p ); // ill-formed static_cast interpretation }— end example
struct S { S() : i(0) { } mutable int i; }; void f() { const S cs; int S::* pm = &S::i; // pm refers to mutable member S::i cs.*pm = 88; // error: cs is a const object }
(ptr_to_obj->*ptr_to_mfct)(10);calls the member function denoted by ptr_to_mfct for the object pointed to by ptr_to_obj.
multiplicative-expression: pm-expression multiplicative-expression * pm-expression multiplicative-expression / pm-expression multiplicative-expression % pm-expression
additive-expression: multiplicative-expression additive-expression + multiplicative-expression additive-expression - multiplicative-expression
shift-expression: additive-expression shift-expression << additive-expression shift-expression >> additive-expression
compare-expression: shift-expression compare-expression <=> shift-expression
relational-expression: compare-expression relational-expression < compare-expression relational-expression > compare-expression relational-expression <= compare-expression relational-expression >= compare-expressionThe lvalue-to-rvalue ([conv.lval]), array-to-pointer ([conv.array]), and function-to-pointer ([conv.func]) standard conversions are performed on the operands.
equality-expression: relational-expression equality-expression == relational-expression equality-expression != relational-expression
struct A {}; struct B : A { int x; }; struct C : A { int x; }; int A::*bx = (int(A::*))&B::x; int A::*cx = (int(A::*))&C::x; bool b1 = (bx == cx); // unspecified— end example
struct B { int f(); }; struct L : B { }; struct R : B { }; struct D : L, R { }; int (B::*pb)() = &B::f; int (L::*pl)() = pb; int (R::*pr)() = pb; int (D::*pdl)() = pl; int (D::*pdr)() = pr; bool x = (pdl == pdr); // false bool y = (pb == pl); // true— end example
and-expression: equality-expression and-expression & equality-expression
exclusive-or-expression: and-expression exclusive-or-expression ^ and-expression
inclusive-or-expression: exclusive-or-expression inclusive-or-expression | exclusive-or-expression
logical-and-expression: inclusive-or-expression logical-and-expression && inclusive-or-expression
logical-or-expression: logical-and-expression logical-or-expression || logical-and-expression
conditional-expression: logical-or-expression logical-or-expression ? expression : assignment-expression
yield-expression: co_yield assignment-expression co_yield braced-init-list
template <typename T> struct my_generator { struct promise_type { T current_value; /* ... */ auto yield_value(T v) { current_value = std::move(v); return std::suspend_always{}; } }; struct iterator { /* ... */ }; iterator begin(); iterator end(); }; my_generator<pair<int,int>> g1() { for (int i = i; i < 10; ++i) co_yield {i,i}; } my_generator<pair<int,int>> g2() { for (int i = i; i < 10; ++i) co_yield make_pair(i,i); } auto f(int x = co_yield 5); // error: yield-expression outside of function suspension context int a[] = { co_yield 1 }; // error: yield-expression outside of function suspension context int main() { auto r1 = g1(); auto r2 = g2(); assert(std::equal(r1.begin(), r1.end(), r2.begin(), r2.end())); }— end example
throw-expression: throw assignment-expression
try { // ... } catch (...) { // catch all exceptions // respond (partially) to exception throw; // pass the exception to some other handler }
assignment-expression: conditional-expression yield-expression throw-expression logical-or-expression assignment-operator initializer-clause
assignment-operator: one of = *= /= %= += -= >>= <<= &= ^= |=
complex<double> z; z = { 1,2 }; // meaning z.operator=({1,2}) z += { 1, 2 }; // meaning z.operator+=({1,2}) int a, b; a = b = { 1 }; // meaning a=b=1; a = { 1 } = b; // syntax error— end example
expression: assignment-expression expression , assignment-expression
constant-expression: conditional-expression
void g() { const int n = 0; [=] { constexpr int i = n; // OK, n is not odr-used here constexpr int j = *&n; // error: &n would be an odr-use of n }; }— end example
auto monad = [](auto v) { return [=] { return v; }; }; auto bind = [](auto m) { return [=](auto fvm) { return fvm(m()); }; }; // OK to capture objects with automatic storage duration created during constant expression evaluation. static_assert(bind(monad(2))(monad)() == monad(2)());— end example
int x; // not constant struct A { constexpr A(bool b) : m(b?42:x) { } int m; }; constexpr int v = A(true).m; // OK: constructor call initializes m with the value 42 constexpr int w = A(false).m; // error: initializer for m is x, which is non-constant constexpr int f1(int k) { constexpr int x = k; // error: x is not initialized by a constant expression // because lifetime of k began outside the initializer of x return x; } constexpr int f2(int k) { int x = k; // OK: not required to be a constant expression // because x is not constexpr return x; } constexpr int incr(int &n) { return ++n; } constexpr int g(int k) { constexpr int x = incr(k); // error: incr(k) is not a core constant expression // because lifetime of k began outside the expression incr(k) return x; } constexpr int h(int k) { int x = incr(k); // OK: incr(k) is not required to be a core constant expression return x; } constexpr int y = h(1); // OK: initializes y with the value 2 // h(1) is a core constant expression because // the lifetime of k begins inside h(1)— end example
struct A { constexpr A(int i) : val(i) { } constexpr operator int() const { return val; } constexpr operator long() const { return 42; } private: int val; }; constexpr A a = alignof(int); alignas(a) int n; // error: ambiguous conversion struct B { int n : a; }; // error: ambiguous conversion— end example
consteval int f() { return 42; } consteval auto g() { return f; } consteval int h(int (*p)() = g()) { return p(); } constexpr int r = h(); // OK constexpr auto e = g(); // error: a pointer to an immediate function is // not a permitted result of a constant expression— end example
template<bool> struct X {}; X<std::is_constant_evaluated()> x; // type X<true> int y; const int a = std::is_constant_evaluated() ? y : 1; // dynamic initialization to 1 double z[a]; // error: a is not usable // in constant expressions const int b = std::is_constant_evaluated() ? 2 : y; // static initialization to 2 int c = y + (std::is_constant_evaluated() ? 2 : y); // dynamic initialization to y+y constexpr int f() { const int n = std::is_constant_evaluated() ? 13 : 17; // n is 13 int m = std::is_constant_evaluated() ? 13 : 17; // m might be 13 or 17 (see below) char arr[n] = {}; // char[13] return m + sizeof(arr); } int p = f(); // m is 13; initialized to 26 int q = p + f(); // m is 17 for this call; initialized to 56— end example
statement: labeled-statement attribute-specifier-seq expression-statement attribute-specifier-seq compound-statement attribute-specifier-seq selection-statement attribute-specifier-seq iteration-statement attribute-specifier-seq jump-statement declaration-statement attribute-specifier-seq try-block init-statement: expression-statement simple-declaration condition: expression attribute-specifier-seq decl-specifier-seq declarator brace-or-equal-initializer
labeled-statement: attribute-specifier-seq identifier : statement attribute-specifier-seq case constant-expression : statement attribute-specifier-seq default : statement
expression-statement: expression ;
compound-statement: { statement-seq }
statement-seq: statement statement-seq statement
selection-statement: if constexpr ( init-statement condition ) statement if constexpr ( init-statement condition ) statement else statement switch ( init-statement condition ) statement
template<typename T, typename ... Rest> void g(T&& p, Rest&& ...rs) { // ... handle p if constexpr (sizeof...(rs) > 0) g(rs...); // never instantiated with an empty argument list } extern int x; // no definition of x required int f() { if constexpr (true) return 0; else if (x) return x; else return -x; }— end example
if constexpr ( init-statement condition ) statementis equivalent to
{ init-statement if constexpr ( condition ) statement }and an if statement of the form
if constexpr ( init-statement condition ) statement else statementis equivalent to
{ init-statement if constexpr ( condition ) statement else statement }except that names declared in the init-statement are in the same declarative region as those declared in the condition.
case constant-expression :where the constant-expression shall be a converted constant expression of the adjusted type of the switch condition.
switch ( init-statement condition ) statementis equivalent to
{ init-statement switch ( condition ) statement }except that names declared in the init-statement are in the same declarative region as those declared in the condition.
iteration-statement: while ( condition ) statement do statement while ( expression ) ; for ( init-statement condition ; expression ) statement for ( init-statement for-range-declaration : for-range-initializer ) statement
for-range-declaration: attribute-specifier-seq decl-specifier-seq declarator attribute-specifier-seq decl-specifier-seq ref-qualifier [ identifier-list ]
for-range-initializer: expr-or-braced-init-list
void f() { for (int i = 0; i < 10; ++i) int i = 0; // error: redeclaration for (int i : { 1, 2, 3 }) int i = 1; // error: redeclaration }— end example
label : { if ( condition ) { statement goto label ; } }
struct A { int val; A(int i) : val(i) { } ~A() { } operator bool() { return val != 0; } }; int i = 1; while (A a = i) { // ... i = 0; }In the while-loop, the constructor and destructor are each called twice, once for the condition that succeeds and once for the condition that fails. — end example
for ( init-statement condition ; expression ) statementis equivalent to
{ init-statement while ( condition ) { statement expression ; } }except that names declared in the init-statement are in the same declarative region as those declared in the condition, and except that a continue in statement (not enclosed in another iteration statement) will execute expression before re-evaluating condition.
int i = 42; int a[10]; for (int i = 0; i < 10; i++) a[i] = i; int j = i; // j = 42— end example
for ( init-statement for-range-declaration : for-range-initializer ) statementis equivalent to
{ init-statement auto &&range = for-range-initializer ; auto begin = begin-expr ; auto end = end-expr ; for ( ; begin != end; ++begin ) { for-range-declaration = * begin ; statement } }where
int array[5] = { 1, 2, 3, 4, 5 }; for (int& x : array) x *= 2;— end example
jump-statement: break ; continue ; return expr-or-braced-init-list ; coroutine-return-statement goto identifier ;
while (foo) { { // ... } contin: ; }
do { { // ... } contin: ; } while (foo);
for (;;) { { // ... } contin: ; }
std::pair<std::string,int> f(const char* p, int x) { return {p,x}; }— end example
class A { ~A() {} }; A f() { return A(); } // error: destructor of A is private (even though it is never invoked)— end example
coroutine-return-statement: co_return expr-or-braced-init-list ;
{ S; goto final-suspend; }where final-suspend is the exposition-only label defined in [dcl.fct.def.coroutine] and S is defined as follows:
declaration-statement: block-declaration
void f() { // ... goto lx; // error: jump into scope of a // ... ly: X a = 1; // ... lx: goto ly; // OK, jump implies destructor call for a followed by // construction again immediately following label ly }— end example
int foo(int i) { static int s = foo(2*i); // undefined behavior: recursive call return i+1; }— end example
T(a)->m = 7; // expression-statement T(a)++; // expression-statement T(a,5)<<c; // expression-statement T(*d)(int); // declaration T(e)[5]; // declaration T(f) = { 1, 2 }; // declaration T(*g)(double(3)); // declaration
class T { // ... public: T(); T(int); T(int, int); }; T(a); // declaration T(*b)(); // declaration T(c)=7; // declaration T(d),e,f=3; // declaration extern int h; T(g)(h,2); // declaration— end example
struct T1 { T1 operator()(int x) { return T1(x); } int operator=(int x) { return x; } T1(int) { } }; struct T2 { T2(int){ } }; int a, (*(*b)(T2))(int), c, d; void f() { // disambiguation requires this to be parsed as a declaration: T1(a) = 3, T2(4), // T2 will be declared as a variable of type T1, but this will not (*(*b)(T2(c)))(int(d)); // allow the last part of the declaration to parse properly, // since it depends on T2 being a type-name }— end example
declaration-seq: declaration declaration-seq declaration
declaration: block-declaration nodeclspec-function-declaration function-definition template-declaration deduction-guide explicit-instantiation explicit-specialization export-declaration linkage-specification namespace-definition empty-declaration attribute-declaration module-import-declaration
block-declaration: simple-declaration asm-declaration namespace-alias-definition using-declaration using-enum-declaration using-directive static_assert-declaration alias-declaration opaque-enum-declaration
nodeclspec-function-declaration: attribute-specifier-seq declarator ;
alias-declaration: using identifier attribute-specifier-seq = defining-type-id ;
simple-declaration: decl-specifier-seq init-declarator-list ; attribute-specifier-seq decl-specifier-seq init-declarator-list ; attribute-specifier-seq decl-specifier-seq ref-qualifier [ identifier-list ] initializer ;
static_assert-declaration: static_assert ( constant-expression ) ; static_assert ( constant-expression , string-literal ) ;
empty-declaration: ;
attribute-declaration: attribute-specifier-seq ;
attribute-specifier-seq decl-specifier-seq init-declarator-list ;is divided into three parts.
[[noreturn]] void f [[noreturn]] (); // OK— end example
enum { }; // error typedef class { }; // error— end example
static_assert(sizeof(int) == sizeof(void*), "wrong pointer size");— end example
decl-specifier: storage-class-specifier defining-type-specifier function-specifier friend typedef constexpr consteval constinit inline
decl-specifier-seq: decl-specifier attribute-specifier-seq decl-specifier decl-specifier-seq
typedef char* Pc; static Pc; // error: name missing
void f(const Pc); // void f(char* const) (not const char*) void g(const int Pc); // void g(const int)
storage-class-specifier: static thread_local extern mutable
static char* f(); // f() has internal linkage char* f() // f() still has internal linkage { /* ... */ } char* g(); // g() has external linkage static char* g() // error: inconsistent linkage { /* ... */ } void h(); inline void h(); // external linkage inline void l(); void l(); // external linkage inline void m(); extern void m(); // external linkage static void n(); inline void n(); // internal linkage static int a; // a has internal linkage int a; // error: two definitions static int b; // b has internal linkage extern int b; // b still has internal linkage int c; // c has external linkage static int c; // error: inconsistent linkage extern int d; // d has external linkage static int d; // error: inconsistent linkage— end example
struct S; extern S a; extern S f(); extern void g(S); void h() { g(a); // error: S is incomplete f(); // error: S is incomplete }— end example
class X { mutable const int* p; // OK mutable int* const q; // error };— end example
function-specifier: virtual explicit-specifier
explicit-specifier: explicit ( constant-expression ) explicit
typedef-name: identifier simple-template-id
using handler_t = void (*)(int); extern handler_t ignore; extern void (*ignore)(int); // redeclare ignore using cell = pair<void*, cell*>; // error— end example
typedef struct s { /* ... */ } s; typedef int I; typedef int I; typedef I I;— end example
struct S { typedef struct A { } A; // OK typedef struct B B; // OK typedef A A; // error };— end example
struct S; typedef struct S S; int main() { struct S* p; // OK } struct S { }; // OK— end example
class complex { /* ... */ }; typedef int complex; // error: redefinition— end example
typedef int complex; class complex { /* ... */ }; // error: redefinition— end example
struct S { S(); ~S(); }; typedef struct S T; S a = T(); // OK struct T * p; // error— end example
typedef struct { } *ps, S; // S is the class name for linkage purposes typedef decltype([]{}) C; // the closure type has no name for linkage purposes— end example
typedef struct { int f() {} } X; // error: struct with typedef name for linkage has member functions— end example
constexpr void square(int &x); // OK: declaration constexpr int bufsz = 1024; // OK: definition constexpr struct pixel { // error: pixel is a type int x; int y; constexpr pixel(int); // OK: declaration }; constexpr pixel::pixel(int a) : x(a), y(x) // OK: definition { square(x); } constexpr pixel small(2); // error: square not defined, so small(2) // not constant ([expr.const]) so constexpr not satisfied constexpr void square(int &x) { // OK: definition x *= x; } constexpr pixel large(4); // OK: square defined int next(constexpr int x) { // error: not for parameters return x + 1; } extern constexpr int memsz; // error: not a definition— end example
constexpr int square(int x) { return x * x; } // OK constexpr long long_max() { return 2147483647; } // OK constexpr int abs(int x) { if (x < 0) x = -x; return x; // OK } constexpr int first(int n) { static int value = n; // error: variable has static storage duration return value; } constexpr int uninit() { struct { int a; } s; return s.a; // error: uninitialized read of s.a } constexpr int prev(int x) { return --x; } // OK constexpr int g(int x, int n) { // OK int r = 1; while (--n > 0) r *= x; return r; }— end example
struct Length { constexpr explicit Length(int i = 0) : val(i) { } private: int val; };— end example
constexpr int f(bool b) { return b ? throw 0 : 0; } // OK constexpr int f() { return f(true); } // ill-formed, no diagnostic required struct B { constexpr B(int x) : i(0) { } // x is unused int i; }; int global; struct D : B { constexpr D() : B(global) { } // ill-formed, no diagnostic required // lvalue-to-rvalue conversion on non-constant global };— end example
constexpr int bar(int x, int y) // OK { return x + y + x*y; } // ... int bar(int x, int y) // error: redefinition of bar { return x * 2 + 3 * y; }— end example
struct pixel { int x, y; }; constexpr pixel ur = { 1294, 1024 }; // OK constexpr pixel origin; // error: initializer missing— end example
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
init-declarator-list: init-declarator init-declarator-list , init-declarator
init-declarator: declarator initializer declarator requires-clause
T D1, D2, ... Dn;is usually equivalent to
T D1; T D2; ... T Dn;where T is a decl-specifier-seq and each Di is an init-declarator.
struct S { /* ... */ }; S S, T; // declare two instances of struct Swhich is not equivalent to
struct S { /* ... */ }; S S; S T; // error
auto i = 1, j = 2.0; // error: deduced types for i and j do not matchas opposed to
auto i = 1; // OK: i deduced to have type int auto j = 2.0; // OK: j deduced to have type double
void f1(int a) requires true; // error: non-templated function template<typename T> auto f2(T a) -> bool requires true; // OK template<typename T> auto f3(T a) requires true -> bool; // error: requires-clause precedes trailing-return-type void (*pf)() requires true; // error: constraint on a variable void g(int (*)() requires true); // error: constraint on a parameter-declaration auto* p = new void(*)(char) requires true; // error: not a function declaration— end example
declarator: ptr-declarator noptr-declarator parameters-and-qualifiers trailing-return-type
ptr-declarator: noptr-declarator ptr-operator ptr-declarator
noptr-declarator: declarator-id attribute-specifier-seq noptr-declarator parameters-and-qualifiers noptr-declarator [ constant-expression ] attribute-specifier-seq ( ptr-declarator )
parameters-and-qualifiers: ( parameter-declaration-clause ) cv-qualifier-seq ref-qualifier noexcept-specifier attribute-specifier-seq
trailing-return-type: -> type-id
ptr-operator: * attribute-specifier-seq cv-qualifier-seq & attribute-specifier-seq && attribute-specifier-seq nested-name-specifier * attribute-specifier-seq cv-qualifier-seq
cv-qualifier-seq: cv-qualifier cv-qualifier-seq
cv-qualifier: const volatile
ref-qualifier: & &&
declarator-id: ... id-expression
type-id: type-specifier-seq abstract-declarator
defining-type-id: defining-type-specifier-seq abstract-declarator
abstract-declarator: ptr-abstract-declarator noptr-abstract-declarator parameters-and-qualifiers trailing-return-type abstract-pack-declarator
ptr-abstract-declarator: noptr-abstract-declarator ptr-operator ptr-abstract-declarator
noptr-abstract-declarator: noptr-abstract-declarator parameters-and-qualifiers noptr-abstract-declarator [ constant-expression ] attribute-specifier-seq ( ptr-abstract-declarator )
abstract-pack-declarator: noptr-abstract-pack-declarator ptr-operator abstract-pack-declarator
noptr-abstract-pack-declarator: noptr-abstract-pack-declarator parameters-and-qualifiers noptr-abstract-pack-declarator [ constant-expression ] attribute-specifier-seq ...
int // int i int * // int *pi int *[3] // int *p[3] int (*)[3] // int (*p3i)[3] int *() // int *f() int (*)(double) // int (*pf)(double)name respectively the types “int”, “pointer to int”, “array of 3 pointers to int”, “pointer to array of 3 int”, “function of (no parameters) returning pointer to int”, and “pointer to a function of (double) returning int”.
struct S { S(int); }; void foo(double a) { S w(int(a)); // function declaration S x(int()); // function declaration S y((int(a))); // object declaration S y((int)a); // object declaration S z = int(a); // object declaration }— end example
template <class T> struct X {}; template <int N> struct Y {}; X<int()> a; // type-id X<int(1)> b; // expression (ill-formed) Y<int()> c; // type-id (ill-formed) Y<int(1)> d; // expression void foo(signed char a) { sizeof(int()); // type-id (ill-formed) sizeof(int(a)); // expression sizeof(int(unsigned(a))); // type-id (ill-formed) (int())+1; // type-id (ill-formed) (int(a))+1; // expression (int(unsigned(a)))+1; // type-id (ill-formed) }— end example
class C { }; void f(int(C)) { } // void f(int(*fp)(C c)) { } // not: void f(int C) { } int g(C); void foo() { f(1); // error: cannot convert 1 to function pointer f(g); // OK }
class C { }; void h(int *(C[10])); // void h(int *(*_fp)(C _parm[10])); // not: void h(int *C[10]);
T Dwhere T is of the form attribute-specifier-seq decl-specifier-seq and D is a declarator.
int unsigned i;the type specifiers int unsigned determine the type “unsigned int” ([dcl.type.simple]).
( D1 )the type of the contained declarator-id is the same as that of the contained declarator-id in the declaration
T D1
* attribute-specifier-seq cv-qualifier-seq D1and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is “derived-declarator-type-list cv-qualifier-seq pointer to T”.
const int ci = 10, *pc = &ci, *const cpc = pc, **ppc; int i, *p, *const cp = &i;declare ci, a constant integer; pc, a pointer to a constant integer; cpc, a constant pointer to a constant integer; ppc, a pointer to a pointer to a constant integer; i, an integer; p, a pointer to integer; and cp, a constant pointer to integer.
i = ci; *cp = ci; pc++; pc = cpc; pc = p; ppc = &pc;
ci = 1; // error ci++; // error *pc = 2; // error cp = &ci; // error cpc++; // error p = pc; // error ppc = &p; // error
*ppc = &ci; // OK, but would make p point to ci because of previous error *p = 5; // clobber ci
& attribute-specifier-seq D1 && attribute-specifier-seq D1and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is “derived-declarator-type-list reference to T”.
typedef int& A; const A aref = 3; // error: lvalue reference to non-const initialized with rvalue— end example
void f(double& a) { a += 3.14; } // ... double d = 0; f(d);declares a to be a reference parameter of f so the call f(d) will add 3.14 to d.
int v[20]; // ... int& g(int i) { return v[i]; } // ... g(3) = 7;declares the function g() to return a reference to an integer so g(3)=7 will assign 7 to the fourth element of the array v.
struct link { link* next; }; link* first; void h(link*& p) { // p is a reference to pointer p->next = first; first = p; p = 0; } void k() { link* q = new link; h(q); }declares p to be a reference to a pointer to link so h(q) will leave q with the value zero.
int i; typedef int& LRI; typedef int&& RRI; LRI& r1 = i; // r1 has the type int& const LRI& r2 = i; // r2 has the type int& const LRI&& r3 = i; // r3 has the type int& RRI& r4 = i; // r4 has the type int& RRI&& r5 = 5; // r5 has the type int&& decltype(r2)& r6 = i; // r6 has the type int& decltype(r2)&& r7 = i; // r7 has the type int&— end example
nested-name-specifier * attribute-specifier-seq cv-qualifier-seq D1and the nested-name-specifier denotes a class, and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is “derived-declarator-type-list cv-qualifier-seq pointer to member of class nested-name-specifier of type T”.
struct X { void f(int); int a; }; struct Y; int X::* pmi = &X::a; void (X::* pmf)(int) = &X::f; double X::* pmd; char Y::* pmc;declares pmi, pmf, pmd and pmc to be a pointer to a member of X of type int, a pointer to a member of X of type void(int), a pointer to a member of X of type double and a pointer to a member of Y of type char respectively.
X obj; // ... obj.*pmi = 7; // assign 7 to an integer member of obj (obj.*pmf)(7); // call a function member of obj with the argument 7
D1 [ constant-expression ] attribute-specifier-seqand the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, the type of the declarator-id in D is “derived-declarator-type-list array of N T”.
D1 [ ] attribute-specifier-seqand the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, the type of the declarator-id in D is “derived-declarator-type-list array of unknown bound of T”, except as specified below.
typedef int A[5], AA[2][3]; typedef const A CA; // type is “array of 5 const int” typedef const AA CAA; // type is “array of 2 array of 3 const int”— end example
extern int x[10]; struct S { static int y[10]; }; int x[]; // OK: bound is 10 int S::y[]; // OK: bound is 10 void f() { extern int x[]; int i = sizeof(x); // error: incomplete object type }— end example
int x3d[3][5][7];declares an array of three elements, each of which is an array of five elements, each of which is an array of seven integers.
D1 ( parameter-declaration-clause ) cv-qualifier-seq ref-qualifier noexcept-specifier attribute-specifier-seqand the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, the type of the declarator-id in D is “derived-declarator-type-list noexcept function of parameter-type-list cv-qualifier-seq ref-qualifier returning T”, where
D1 ( parameter-declaration-clause ) cv-qualifier-seq ref-qualifier noexcept-specifier attribute-specifier-seq trailing-return-typeand the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, T shall be the single type-specifier auto.
parameter-declaration-clause: parameter-declaration-list ... parameter-declaration-list , ...
parameter-declaration-list: parameter-declaration parameter-declaration-list , parameter-declaration
parameter-declaration: attribute-specifier-seq decl-specifier-seq declarator attribute-specifier-seq decl-specifier-seq declarator = initializer-clause attribute-specifier-seq decl-specifier-seq abstract-declarator attribute-specifier-seq decl-specifier-seq abstract-declarator = initializer-clause
typedef int FIC(int) const; FIC f; // error: does not declare a member function struct S { FIC f; // OK }; FIC S::*pm = &S::f; // OK— end example
typedef void F(); struct S { const F f; // OK: equivalent to: void f(); };— end example
int fseek(FILE*, long, int);declares a function taking three arguments of the specified types, and returning int ([dcl.type]).
typedef void F(); F fv; // OK: equivalent to void fv(); F fv { } // error void fv() { } // OK: definition of fv— end example
int i, *pi, f(), *fpi(int), (*pif)(const char*, const char*), (*fpif(int))(int);declares an integer i, a pointer pi to an integer, a function f taking no arguments and returning an integer, a function fpi taking an integer argument and returning a pointer to an integer, a pointer pif to a function which takes two pointers to constant characters and returns an integer, a function fpif taking an integer argument and returning a pointer to a function that takes an integer argument and returns an integer.
typedef int IFUNC(int); IFUNC* fpif(int);or
auto fpif(int)->int(*)(int);
template <class T, class U> auto add(T t, U u) -> decltype(t + u);rather than
template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);
template<typename T> concept C1 = /* ... */; template<typename T> concept C2 = /* ... */; template<typename... Ts> concept C3 = /* ... */; void g1(const C1 auto*, C2 auto&); void g2(C1 auto&...); void g3(C3 auto...); void g4(C3 auto);
template<C1 T, C2 U> void g1(const T*, U&); template<C1... Ts> void g2(Ts&...); template<C3... Ts> void g3(Ts...); template<C3 T> void g4(T);
template<> void g1<int>(const int*, const double&); // OK, specialization of g1<int, const double>— end example
template<typename> concept C = /* ... */; template <typename T, C U> void g(T x, U y, C auto z);
template<typename T, C U, C W> void g(T x, U y, W z); template<typename T, typename U, typename W> requires C<U> && C<W> void g(T x, U y, W z);— end example
template<typename... T> void f(T (* ...t)(int, int)); int add(int, int); float subtract(int, int); void g() { f(add, subtract); }— end example
void g(int = 0, ...); // OK, ellipsis is not a parameter so it can follow // a parameter with a default argument void f(int, int); void f(int, int = 7); void h() { f(3); // OK, calls f(3, 7) void f(int = 1, int); // error: does not use default from surrounding scope } void m() { void f(int, int); // has no defaults f(4); // error: wrong number of arguments void f(int, int = 5); // OK f(4); // OK, calls f(4, 5); void f(int, int = 5); // error: cannot redefine, even to same value } void n() { f(6); // OK, calls f(6, 7) } template<class ... T> struct C { void f(int n = 0, T...); }; C<int> c; // OK, instantiates declaration void C::f(int n = 0, int)— end example
class C { void f(int i = 3); void g(int i, int j = 99); }; void C::f(int i = 3) {} // error: default argument already specified in class scope void C::g(int i = 88, int j) {} // in this translation unit, C::g can be called with no argument— end example
void f() { int i; extern void g(int x = i); // error extern void h(int x = sizeof(i)); // OK // ... }— end example
int a; int f(int a, int b = a); // error: parameter a used as default argument typedef int I; int g(float I, int b = I(2)); // error: parameter I found int h(int a, int b = sizeof(a)); // OK, unevaluated operand— end example
int b; class X { int a; int mem1(int i = a); // error: non-static member a used as default argument int mem2(int i = b); // OK; use X::b static int b; };
int f(int = 0); void h() { int j = f(1); int k = f(); // OK, means f(0) } int (*p1)(int) = &f; int (*p2)() = &f; // error: type mismatch— end example
struct A { virtual void f(int a = 7); }; struct B : public A { void f(int a); }; void m() { B* pb = new B; A* pa = pb; pa->f(); // OK, calls pa->B::f(7) pb->f(); // error: wrong number of arguments for B::f() }— end example
initializer: brace-or-equal-initializer ( expression-list )
brace-or-equal-initializer: = initializer-clause braced-init-list
initializer-clause: assignment-expression braced-init-list
braced-init-list: { initializer-list , } { designated-initializer-list , } { }
initializer-list: initializer-clause ... initializer-list , initializer-clause ...
designated-initializer-list: designated-initializer-clause designated-initializer-list , designated-initializer-clause
designated-initializer-clause: designator brace-or-equal-initializer
designator: . identifier
expr-or-braced-init-list: expression braced-init-list
int f(int); int a = 2; int b = f(a); int c(b);— end example
X a();is not the declaration of an object of class X, but the declaration of a function taking no argument and returning an X.
int a; struct X { static int a; static int b; }; int X::a = 1; int X::b = a; // X::b = X::a— end example
struct A { int a; int&& r; }; int f(); int n = 10; A a1{1, f()}; // OK, lifetime is extended A a2(1, f()); // well-formed, but dangling reference A a3{1.0, 1}; // error: narrowing conversion A a4(1.0, 1); // well-formed, but dangling reference A a5(1.0, std::move(n)); // OK— end example
struct C { union { int a; const char* p; }; int x; } c = { .a = 1, .x = 3 };initializes c.a with 1 and c.x with 3. — end example
struct A { int x; struct B { int i; int j; } b; } a = { 1, { 2, 3 } };initializes a.x with 1, a.b.i with 2, a.b.j with 3.
struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1{{1, 2}, {}, 4}; derived d2{{}, {}, 4};initializes d1.b1 with 1, d1.b2 with 2, d1.b3 with 42, d1.d with 4, and d2.b1 with 0, d2.b2 with 42, d2.b3 with 42, d2.d with 4. — end example
struct S { int a; const char* b; int c; int d = b[a]; }; S ss = { 1, "asdf" };initializes ss.a with 1, ss.b with "asdf", ss.c with the value of an expression of the form int{} (that is, 0), and ss.d with the value of ss.b[ss.a] (that is, 's'), and in
struct X { int i, j, k = 42; }; X a[] = { 1, 2, 3, 4, 5, 6 }; X b[2] = { { 1, 2, 3 }, { 4, 5, 6 } };a and b have the same value
struct A { string a; int b = 42; int c = -1; };
int x[] = { 1, 3, 5 };declares and initializes x as a one-dimensional array that has three elements since no size was specified and there are three initializers.
struct S { int y[] = { 0 }; // error: non-static data member of incomplete type };— end example
struct A; extern A a; struct A { const A& a1 { A{a,a} }; // OK const A& a2 { A{} }; // error }; A a{a,a}; // OK struct B { int n = B{}.n; // error };— end example
struct S { } s; struct A { S s1; int i1; S s2; int i2; S s3; int i3; } a = { { }, // Required initialization 0, s, // Required initialization 0 }; // Initialization not required for A::s3 because A::i3 is also not initialized— end example
float y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, };is a completely-braced initialization: 1, 3, and 5 initialize the first row of the array y[0], namely y[0][0], y[0][1], and y[0][2].
float y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 };
union u { int a; const char* b; }; u a = { 1 }; u b = a; u c = 1; // error u d = { 0, "asdf" }; // error u e = { "asdf" }; // error u f = { .b = "asdf" }; u g = { .a = 1, .b = "asdf" }; // error— end example
char msg[] = "Syntax error on line %s\n";shows a character array whose members are initialized with a string-literal.
int g(int) noexcept; void f() { int i; int& r = i; // r refers to i r = 1; // the value of i becomes 1 int* p = &r; // p points to i int& rr = r; // rr refers to what r refers to, that is, to i int (&rg)(int) = g; // rg refers to the function g rg(i); // calls function g int a[3]; int (&ra)[3] = a; // ra refers to the array a ra[1] = i; // modifies a[1] }— end example
int& r1; // error: initializer missing extern int& r2; // OK— end example
double d = 2.0; double& rd = d; // rd refers to d const double& rcd = d; // rcd refers to d struct A { }; struct B : A { operator int&(); } b; A& ra = b; // ra refers to A subobject in b const A& rca = b; // rca refers to A subobject in b int& ir = B(); // ir refers to the result of B::operator int&— end example
double& rd2 = 2.0; // error: not an lvalue and reference not const int i = 2; double& rd3 = i; // error: type mismatch and reference not const— end example
struct A { }; struct B : A { } b; extern B f(); const A& rca2 = f(); // bound to the A subobject of the B rvalue. A&& rra = f(); // same as above struct X { operator B(); operator int&(); } x; const A& r = x; // bound to the A subobject of the result of the conversion int i2 = 42; int&& rri = static_cast<int&&>(i2); // bound directly to i2 B&& rrb = x; // bound directly to the result of operator B— end example
struct Banana { }; struct Enigma { operator const Banana(); }; struct Alaska { operator Banana&(); }; void enigmatic() { typedef const Banana ConstBanana; Banana &&banana1 = ConstBanana(); // error Banana &&banana2 = Enigma(); // error Banana &&banana3 = Alaska(); // error } const double& rcd2 = 2; // rcd2 refers to temporary with value 2.0 double&& rrd = 2; // rrd refers to temporary with value 2.0 const volatile int cvi = 1; const int& r2 = cvi; // error: cv-qualifier dropped struct A { operator volatile int&(); } a; const int& r3 = a; // error: cv-qualifier dropped // from result of conversion function double d2 = 1.0; double&& rrd2 = d2; // error: initializer is lvalue of related type struct X { operator int&(); }; int&& rri2 = X(); // error: result of conversion function is lvalue of related type int i3 = 2; double&& rrd3 = i3; // rrd3 refers to temporary with value 2.0— end example
int a = {1}; std::complex<double> z{1,2}; new std::vector<std::string>{"once", "upon", "a", "time"}; // 4 string elements f( {"Nicholas","Annemarie"} ); // pass list of two elements return { "Norah" }; // return list of one element int* e {}; // initialization to zero / null pointer x = double{1}; // explicitly construct a double std::map<std::string,int> anim = { {"bear",4}, {"cassowary",2}, {"tiger",7} };— end example
struct A { int x; int y; int z; }; A a{.y = 2, .x = 1}; // error: designator order does not match declaration order A b{.x = 1, .z = 2}; // OK, b.y initialized to 0— end example
double ad[] = { 1, 2.0 }; // OK int ai[] = { 1, 2.0 }; // error: narrowing struct S2 { int m1; double m2, m3; }; S2 s21 = { 1, 2, 3.0 }; // OK S2 s22 { 1.0, 2, 3 }; // error: narrowing S2 s23 { }; // OK: default to 0,0,0— end example
struct S { S(std::initializer_list<double>); // #1 S(std::initializer_list<int>); // #2 S(); // #3 // ... }; S s1 = { 1.0, 2.0, 3.0 }; // invoke #1 S s2 = { 1, 2, 3 }; // invoke #2 S s3 = { }; // invoke #3— end example
struct Map { Map(std::initializer_list<std::pair<std::string,int>>); }; Map ship = {{"Sophie",14}, {"Surprise",28}};— end example
struct S { // no initializer-list constructors S(int, double, double); // #1 S(); // #2 // ... }; S s1 = { 1, 2, 3.0 }; // OK: invoke #1 S s2 { 1.0, 2, 3 }; // error: narrowing S s3 { }; // OK: invoke #2— end example
enum byte : unsigned char { }; byte b { 42 }; // OK byte c = { 42 }; // error byte d = byte{ 42 }; // OK; same value as b byte e { -1 }; // error struct A { byte b; }; A a1 = { { 42 } }; // error A a2 = { byte{ 42 } }; // OK void f(byte); f({ 42 }); // error enum class Handle : uint32_t { Invalid = 0 }; Handle h { 42 }; // OK— end example
int x1 {2}; // OK int x2 {2.0}; // error: narrowing— end example
struct S { S(std::initializer_list<double>); // #1 S(const std::string&); // #2 // ... }; const S& r1 = { 1, 2, 3.0 }; // OK: invoke #1 const S& r2 { "Spinach" }; // OK: invoke #2 S& r3 = { 1, 2, 3 }; // error: initializer is not an lvalue const int& i1 = { 1 }; // OK const int& i2 = { 1.1 }; // error: narrowing const int (&iar)[2] = { 1, 2 }; // OK: iar is bound to temporary array struct A { } a; struct B { explicit B(const A&); }; const B& b2{a}; // error: cannot copy-list-initialize B temporary from A— end example
int** pp {}; // initialized to null pointer— end example
struct A { int i; int j; }; A a1 { 1, 2 }; // aggregate initialization A a2 { 1.2 }; // error: narrowing struct B { B(std::initializer_list<int>); }; B b1 { 1, 2 }; // creates initializer_list<int> and calls constructor B b2 { 1, 2.0 }; // error: narrowing struct C { C(int i, double j); }; C c1 = { 1, 2.2 }; // calls constructor with arguments (1, 2.2) C c2 = { 1.1, 2 }; // error: narrowing int j { 1 }; // initialize to 1 int k { }; // initialize to 0— end example
struct X { X(std::initializer_list<double> v); }; X x{ 1,2,3 };
const double __a[3] = {double{1}, double{2}, double{3}}; X x(std::initializer_list<double>(__a, __a+3));assuming that the implementation can construct an initializer_list object with a pair of pointers.
typedef std::complex<double> cmplx; std::vector<cmplx> v1 = { 1, 2, 3 }; void f() { std::vector<cmplx> v2{ 1, 2, 3 }; std::initializer_list<int> i3 = { 1, 2, 3 }; } struct A { std::initializer_list<int> i4; A() : i4{ 1, 2, 3 } {} // ill-formed, would create a dangling reference };
int x = 999; // x is not a constant expression const int y = 999; const int z = 99; char c1 = x; // OK, though it might narrow (in this case, it does narrow) char c2{x}; // error: might narrow char c3{y}; // error: narrows (assuming char is 8 bits) char c4{z}; // OK: no narrowing needed unsigned char uc1 = {5}; // OK: no narrowing needed unsigned char uc2 = {-1}; // error: narrows unsigned int ui1 = {-1}; // error: narrows signed int si1 = { (unsigned int)-1 }; // error: narrows int ii = {2.0}; // error: narrows float f1 { x }; // error: might narrow float f2 { 7 }; // OK: 7 can be exactly represented as a float bool b = {"meow"}; // error: narrows int f(int); int a[] = { 2, f(2), f(2.0) }; // OK: the double-to-int conversion is not at the top level— end example
function-definition: attribute-specifier-seq decl-specifier-seq declarator virt-specifier-seq function-body attribute-specifier-seq decl-specifier-seq declarator requires-clause function-body
function-body: ctor-initializer compound-statement function-try-block = default ; = delete ;
int max(int a, int b, int c) { int m = (a > b) ? a : b; return (m > c) ? m : c; }
static const char __func__[] = "function-name";had been provided, where function-name is an implementation-defined string.
struct S { S() : s(__func__) { } // OK const char* s; }; void f(const char* s = __func__); // error: __func__ is undeclared— end example
struct S { constexpr S() = default; // error: implicit S() is not constexpr S(int a = 0) = default; // error: default argument void operator=(const S&) = default; // error: non-matching return type ~S() noexcept(false) = default; // OK, despite mismatched exception specification private: int i; S(S&); // OK: private copy constructor }; S::S(S&) = default; // OK: defines copy constructor struct T { T(); T(T &&) noexcept(false); }; struct U { T t; U(); U(U &&) noexcept = default; }; U u1; U u2 = static_cast<U&&>(u1); // OK, calls std::terminate if T::T(T&&) throws— end example
struct trivial { trivial() = default; trivial(const trivial&) = default; trivial(trivial&&) = default; trivial& operator=(const trivial&) = default; trivial& operator=(trivial&&) = default; ~trivial() = default; }; struct nontrivial1 { nontrivial1(); }; nontrivial1::nontrivial1() = default; // not first declaration— end example
struct onlydouble { onlydouble() = delete; // OK, but redundant template<class T> onlydouble(T) = delete; onlydouble(double); };
struct sometype { void* operator new(std::size_t) = delete; void* operator new[](std::size_t) = delete; }; sometype* p = new sometype; // error: deleted class operator new sometype* q = new sometype[3]; // error: deleted class operator new[]— end example
struct moveonly { moveonly() = default; moveonly(const moveonly&) = delete; moveonly(moveonly&&) = default; moveonly& operator=(const moveonly&) = delete; moveonly& operator=(moveonly&&) = default; ~moveonly() = default; }; moveonly* p; moveonly q(*p); // error: deleted copy constructor— end example
struct sometype { sometype(); }; sometype::sometype() = delete; // error: not first declaration— end example
task<int> f(); task<void> g1() { int i = co_await f(); std::cout << "f() => " << i << std::endl; } template <typename... Args> task<void> g2(Args&&...) { // OK, ellipsis is a pack expansion int i = co_await f(); std::cout << "f() => " << i << std::endl; } task<void> g3(int a, ...) { // error: variable parameter list not allowed int i = co_await f(); std::cout << "f() => " << i << std::endl; }— end example
{ promise-type promise promise-constructor-arguments ; try { co_await promise.initial_suspend() ; function-body } catch ( ... ) { if (!initial-await-resume-called) throw ; promise.unhandled_exception() ; } final-suspend : co_await promise.final_suspend() ; }where
#include <iostream> #include <coroutine> // ::operator new(size_t, nothrow_t) will be used if allocation is needed struct generator { struct promise_type; using handle = std::coroutine_handle<promise_type>; struct promise_type { int current_value; static auto get_return_object_on_allocation_failure() { return generator{nullptr}; } auto get_return_object() { return generator{handle::from_promise(*this)}; } auto initial_suspend() { return std::suspend_always{}; } auto final_suspend() { return std::suspend_always{}; } void unhandled_exception() { std::terminate(); } void return_void() {} auto yield_value(int value) { current_value = value; return std::suspend_always{}; } }; bool move_next() { return coro ? (coro.resume(), !coro.done()) : false; } int current_value() { return coro.promise().current_value; } generator(generator const&) = delete; generator(generator && rhs) : coro(rhs.coro) { rhs.coro = nullptr; } ~generator() { if (coro) coro.destroy(); } private: generator(handle h) : coro(h) {} handle coro; }; generator f() { co_yield 1; co_yield 2; } int main() { auto g = f(); while (g.move_next()) std::cout << g.current_value() << std::endl; }— end example
attribute-specifier-seq S cv A e ;and each element is copy-initialized or direct-initialized from the corresponding element of the assignment-expression as specified by the form of the initializer.
attribute-specifier-seq decl-specifier-seq ref-qualifier e initializer ;where the declaration is never interpreted as a function declaration and the parts of the declaration other than the declarator-id are taken from the corresponding structured binding declaration.
auto f() -> int(&)[2]; auto [ x, y ] = f(); // x and y refer to elements in a copy of the array return value auto& [ xr, yr ] = f(); // xr and yr refer to elements in the array referred to by f's return value— end example
S U r = initializer ;
struct S { int x1 : 2; volatile double y1; }; S f(); const auto [ x, y ] = f();
enum-name: identifier
enum-specifier: enum-head { enumerator-list } enum-head { enumerator-list , }
enum-head: enum-key attribute-specifier-seq enum-head-name enum-base
enum-head-name: nested-name-specifier identifier
opaque-enum-declaration: enum-key attribute-specifier-seq enum-head-name enum-base ;
enum-key: enum enum class enum struct
enum-base: : type-specifier-seq
enumerator-list: enumerator-definition enumerator-list , enumerator-definition
enumerator-definition: enumerator enumerator = constant-expression
enumerator: identifier attribute-specifier-seq
enum color { red, yellow, green=20, blue }; color col = red; color* cp = &col; if (*cp == blue) // ...makes color a type describing various colors, and then declares col as an object of that type, and cp as a pointer to an object of that type.
color c = 1; // error: type mismatch, no conversion from int to color int i = yellow; // OK: yellow converted to integral value 1, integral promotion
enum class Col { red, yellow, green }; int x = Col::red; // error: no Col to int conversion Col y = Col::red; if (y) { } // error: no Col to bool conversion
enum direction { left='l', right='r' }; void g() { direction d; // OK d = left; // OK d = direction::right; // OK } enum class altitude { high='h', low='l' }; void h() { altitude a; // OK a = high; // error: high not in scope a = altitude::low; // OK }— end example
struct X { enum direction { left='l', right='r' }; int f(int i) { return i==left ? 0 : i==right ? 1 : 2; } }; void g(X* p) { direction d; // error: direction not in scope int i; i = p->f(left); // error: left not in scope i = p->f(X::right); // OK i = p->f(p->left); // OK // ... }— end example
using-enum-declaration: using elaborated-enum-specifier ;
enum class fruit { orange, apple }; struct S { using enum fruit; // OK, introduces orange and apple into S }; void f() { S s; s.orange; // OK, names fruit::orange S::orange; // OK, names fruit::orange }— end example
enum class fruit { orange, apple }; enum class color { red, orange }; void f() { using enum fruit; // OK using enum color; // error: color::orange and fruit::orange conflict }— end example
export module M; namespace N1 {} // N1 is not exported export namespace N2 {} // N2 is exported namespace N3 { export int n; } // N3 is exported— end example
namespace-name: identifier namespace-alias
namespace-definition: named-namespace-definition unnamed-namespace-definition nested-namespace-definition
named-namespace-definition: inline namespace attribute-specifier-seq identifier { namespace-body }
unnamed-namespace-definition: inline namespace attribute-specifier-seq { namespace-body }
nested-namespace-definition: namespace enclosing-namespace-specifier :: inline identifier { namespace-body }
enclosing-namespace-specifier: identifier enclosing-namespace-specifier :: inline identifier
namespace-body: declaration-seq
namespace Outer { int i; namespace Inner { void f() { i++; } // Outer::i int i; void g() { i++; } // Inner::i } }— end example
namespace Q { namespace V { void f(); // enclosing namespaces are the global namespace, Q, and Q::V class C { void m(); }; } void V::f() { // enclosing namespaces are the global namespace, Q, and Q::V extern void h(); // ... so this declares Q::V::h } void V::C::m() { // enclosing namespaces are the global namespace, Q, and Q::V } }— end example
namespace E { inline namespace I { B } }where the optional inline is present if and only if the identifier I is preceded by inline.
namespace A::inline B::C { int i; }
namespace A { inline namespace B { namespace C { int i; } } }
inline namespace unique { /* empty body */ } using namespace unique ; namespace unique { namespace-body }where inline appears if and only if it appears in the unnamed-namespace-definition and all occurrences of unique in a translation unit are replaced by the same identifier, and this identifier differs from all other identifiers in the translation unit.
namespace { int i; } // unique::i void f() { i++; } // unique::i++ namespace A { namespace { int i; // A::unique::i int j; // A::unique::j } void g() { i++; } // A::unique::i++ } using namespace A; void h() { i++; // error: unique::i or A::unique::i A::i++; // A::unique::i j++; // A::unique::j }— end example
namespace X { void f() { /* ... */ } // OK: introduces X::f() namespace M { void g(); // OK: introduces X::M::g() } using M::g; void g(); // error: conflicts with X::M::g() }— end example
namespace Q { namespace V { void f(); } void V::f() { /* ... */ } // OK void V::g() { /* ... */ } // error: g() is not yet a member of V namespace V { void g(); } } namespace R { void Q::V::g() { /* ... */ } // error: R doesn't enclose Q }— end example
// Assume f and g have not yet been declared. void h(int); template <class T> void f2(T); namespace A { class X { friend void f(X); // A::f(X) is a friend class Y { friend void g(); // A::g is a friend friend void h(int); // A::h is a friend // ::h not considered friend void f2<>(int); // ::f2<>(int) is a friend }; }; // A::f, A::g and A::h are not visible here X x; void g() { f(x); } // definition of A::g void f(X) { /* ... */ } // definition of A::f void h(int) { /* ... */ } // definition of A::h // A::f, A::g and A::h are visible here and known to be friends } using A::x; void h() { A::f(x); A::X::f(x); // error: f is not a member of A::X A::X::Y::g(); // error: g is not a member of A::X::Y }— end example
namespace-alias: identifier
namespace-alias-definition: namespace identifier = qualified-namespace-specifier ;
qualified-namespace-specifier: nested-name-specifier namespace-name
namespace Company_with_very_long_name { /* ... */ } namespace CWVLN = Company_with_very_long_name; namespace CWVLN = Company_with_very_long_name; // OK: duplicate namespace CWVLN = CWVLN;— end example
using-directive: attribute-specifier-seq using namespace nested-name-specifier namespace-name ;
namespace A { int i; namespace B { namespace C { int i; } using namespace A::B::C; void f1() { i = 5; // OK, C::i visible in B and hides A::i } } namespace D { using namespace B; using namespace C; void f2() { i = 5; // ambiguous, B::C::i or A::i? } } void f3() { i = 5; // uses A::i } } void f4() { i = 5; // error: neither i is visible }— end example
namespace M { int i; } namespace N { int i; using namespace M; } void f() { using namespace N; i = 7; // error: both M::i and N::i are visible }
namespace A { int i; } namespace B { int i; int j; namespace C { namespace D { using namespace A; int j; int k; int a = i; // B::i hides A::i } using namespace D; int k = 89; // no problem yet int l = k; // ambiguous: C::k or D::k int m = i; // B::i hides A::i int n = j; // D::j hides B::j } }
namespace A { class X { }; extern "C" int g(); extern "C++" int h(); } namespace B { void X(int); extern "C" int g(); extern "C++" int h(int); } using namespace A; using namespace B; void f() { X(1); // error: name X found in two namespaces g(); // OK: name g refers to the same entity h(); // OK: overload resolution selects A::h }— end note
namespace D { int d1; void f(char); } using namespace D; int d1; // OK: no conflict with D::d1 namespace E { int e; void f(int); } namespace D { // namespace extension int d2; using namespace E; void f(int); } void f() { d1++; // error: ambiguous ::d1 or D::d1? ::d1++; // OK D::d1++; // OK d2++; // OK: D::d2 e++; // OK: E::e f(1); // error: ambiguous: D::f(int) or E::f(int)? f('a'); // OK: D::f(char) }— end example
using-declaration: using using-declarator-list ;
using-declarator-list: using-declarator ... using-declarator-list , using-declarator ...
using-declarator: typename nested-name-specifier unqualified-id
struct B { void f(char); void g(char); enum E { e }; union { int x; }; }; struct D : B { using B::f; void f(int) { f('c'); } // calls B::f(char) void g(int) { g('c'); } // recursively calls D::g(int) };— end example
enum class button { up, down }; struct S { using button::up; button b = up; // OK };— end example
template <typename... bases> struct X : bases... { using bases::g...; }; X<B, D> x; // OK: B::g and D::g introduced— end example
class C { int g(); }; class D2 : public B { using B::f; // OK: B is a base of D2 using B::e; // OK: e is an enumerator of base B using B::x; // OK: x is a union member of base B using C::g; // error: C isn't a base of D2 };— end example
struct A { template <class T> void f(T); template <class T> struct X { }; }; struct B : A { using A::f<double>; // error using A::X<int>; // error };— end example
struct X { int i; static int s; }; void f() { using X::i; // error: X::i is a class member and this is not a member declaration. using X::s; // error: X::s is a class member and this is not a member declaration. }— end example
void f(); namespace A { void g(); } namespace X { using ::f; // global f using A::g; // A's g } void h() { X::f(); // calls ::f X::g(); // calls A::g }— end example
namespace A { int i; } namespace A1 { using A::i, A::i; // OK: double declaration } struct B { int i; }; struct X : B { using B::i, B::i; // error: double member declaration };— end example
namespace A { void f(int); } using A::f; // f is a synonym for A::f; that is, for A::f(int). namespace A { void f(char); } void foo() { f('a'); // calls f(int), even though f(char) exists. } void bar() { using A::f; // f is a synonym for A::f; that is, for A::f(int) and A::f(char). f('a'); // calls f(char) }— end example
namespace A { int x; } namespace B { int i; struct g { }; struct x { }; void f(int); void f(double); void g(char); // OK: hides struct g } void func() { int i; using B::i; // error: i declared twice void f(char); using B::f; // OK: each f is a function f(3.5); // calls B::f(double) using B::g; g('a'); // calls B::g(char) struct g g1; // g1 has class type B::g using B::x; using A::x; // OK: hides struct B::x x = 99; // assigns to A::x struct x x1; // x1 has class type B::x }— end example
namespace B { void f(int); void f(double); } namespace C { void f(int); void f(double); void f(char); } void h() { using B::f; // B::f(int) and B::f(double) using C::f; // C::f(int), C::f(double), and C::f(char) f('h'); // calls C::f(char) f(1); // error: ambiguous: B::f(int) or C::f(int)? void f(int); // error: f(int) conflicts with C::f(int) and B::f(int) }— end example
struct B { virtual void f(int); virtual void f(char); void g(int); void h(int); }; struct D : B { using B::f; void f(int); // OK: D::f(int) overrides B::f(int); using B::g; void g(char); // OK using B::h; void h(int); // OK: D::h(int) hides B::h(int) }; void k(D* p) { p->f(1); // calls D::f(int) p->f('a'); // calls B::f(char) p->g(1); // calls B::g(int) p->g('a'); // calls D::g(char) } struct B1 { B1(int); }; struct B2 { B2(int); }; struct D1 : B1, B2 { using B1::B1; using B2::B2; }; D1 d1(0); // error: ambiguous struct D2 : B1, B2 { using B1::B1; using B2::B2; D2(int); // OK: D2::D2(int) hides B1::B1(int) and B2::B2(int) }; D2 d2(0); // calls D2::D2(int)— end example
struct A { int x(); }; struct B : A { }; struct C : A { using A::x; int x(int); }; struct D : B, C { using C::x; int x(double); }; int f(D* d) { return d->x(); // error: overload resolution selects A::x, but A is an ambiguous base class }— end example
class A { private: void f(char); public: void f(int); protected: void g(); }; class B : public A { using A::f; // error: A::f(char) is inaccessible public: using A::g; // B::g is a public synonym for A::g };— end example
asm-declaration: attribute-specifier-seq asm ( string-literal ) ;
linkage-specification: extern string-literal { declaration-seq } extern string-literal declaration
complex sqrt(complex); // C++ linkage by default extern "C" { double sqrt(double); // C linkage }— end example
extern "C" // the name f1 and its function type have C language linkage; void f1(void(*pf)(int)); // pf is a pointer to a C function extern "C" typedef void FUNC(); FUNC f2; // the name f2 has C++ language linkage and the // function's type has C language linkage extern "C" FUNC f3; // the name of function f3 and the function's type have C language linkage void (*pf2)(FUNC*); // the name of the variable pf2 has C++ linkage and the type // of pf2 is “pointer to C++ function that takes one parameter of type // pointer to C function” extern "C" { static void f4(); // the name of the function f4 has internal linkage (not C language linkage) // and the function's type has C language linkage. } extern "C" void f5() { extern void f4(); // OK: Name linkage (internal) and function type linkage (C language linkage) // obtained from previous declaration. } extern void f4(); // OK: Name linkage (internal) and function type linkage (C language linkage) // obtained from previous declaration. void f6() { extern void f4(); // OK: Name linkage (internal) and function type linkage (C language linkage) // obtained from previous declaration. }— end example
extern "C" typedef void FUNC_c(); class C { void mf1(FUNC_c*); // the name of the function mf1 and the member function's type have // C++ language linkage; the parameter has type “pointer to C function” FUNC_c mf2; // the name of the function mf2 and the member function's type have // C++ language linkage static FUNC_c* q; // the name of the data member q has C++ language linkage and // the data member's type is “pointer to C function” }; extern "C" { class X { void mf(); // the name of the function mf and the member function's type have // C++ language linkage void mf2(void(*)()); // the name of the function mf2 has C++ language linkage; // the parameter has type “pointer to C function” }; }— end example
int x; namespace A { extern "C" int f(); extern "C" int g() { return 1; } extern "C" int h(); extern "C" int x(); // error: same name as global-space object x } namespace B { extern "C" int f(); // A::f and B::f refer to the same function extern "C" int g() { return 1; } // error: the function g with C language linkage has two definitions } int A::f() { return 98; } // definition for the function f with C language linkage extern "C" int h() { return 97; } // definition for the function h with C language linkage // A::h and ::h refer to the same function— end example
extern "C" double f(); static double f(); // error extern "C" int i; // declaration extern "C" { int i; // definition } extern "C" static void g(); // error— end example
attribute-specifier-seq: attribute-specifier-seq attribute-specifier
attribute-specifier: [ [ attribute-using-prefix attribute-list ] ] alignment-specifier
alignment-specifier: alignas ( type-id ... ) alignas ( constant-expression ... )
attribute-using-prefix: using attribute-namespace :
attribute-list: attribute attribute-list , attribute attribute ... attribute-list , attribute ...
attribute: attribute-token attribute-argument-clause
attribute-token: identifier attribute-scoped-token
attribute-scoped-token: attribute-namespace :: identifier
attribute-namespace: identifier
attribute-argument-clause: ( balanced-token-seq )
balanced-token-seq: balanced-token balanced-token-seq balanced-token
balanced-token: ( balanced-token-seq ) [ balanced-token-seq ] { balanced-token-seq } any token other than a parenthesis, a bracket, or a brace
[[using CC: opt(1), debug]] // same as [[CC::opt(1), CC::debug]] void f() {} [[using CC: opt(1)]] [[CC::debug]] // same as [[CC::opt(1)]] [[CC::debug]] void g() {} [[using CC: CC::opt(1)]] // error: cannot combine using and scoped attribute token void h() {}— end example
int p[10]; void f() { int x = 42, y[5]; int(p[[x] { return x; }()]); // error: invalid attribute on a nested declarator-id and // not a function-style cast of an element of p. y[[] { return 2; }()] = 2; // error even though attributes are not allowed in this context. int i [[vendor::attr([[]])]]; // well-formed implementation-defined attribute. }— end example
struct alignas(8) S {}; struct alignas(1) U { S s; }; // error: U specifies an alignment that is less strict than if the alignas(1) were omitted.— end example
// Translation unit #1: struct S { int x; } s, *p = &s; // Translation unit #2: struct alignas(16) S; // ill-formed, no diagnostic required: definition of S lacks alignment extern S* p;— end example
alignas(T) alignas(A) T buffer[N];
alignas(double) void f(); // error: alignment applied to function alignas(double) unsigned char c[sizeof(double)]; // array of characters, suitably aligned for a double extern unsigned char c[sizeof(double)]; // no alignas necessary alignas(float) extern unsigned char c[sizeof(double)]; // error: different alignment in declaration— end example
/* Translation unit A. */ struct foo { int* a; int* b; }; std::atomic<struct foo *> foo_head[10]; int foo_array[10][10]; [[carries_dependency]] struct foo* f(int i) { return foo_head[i].load(memory_order::consume); } int g(int* x, int* y [[carries_dependency]]) { return kill_dependency(foo_array[*x][*y]); } /* Translation unit B. */ [[carries_dependency]] struct foo* f(int i); int g(int* x, int* y [[carries_dependency]]); int c = 3; void h(int i) { struct foo* p; p = f(i); do_something_with(g(&c, p->a)); do_something_with(g(p->a, &c)); }
( string-literal )
void f(int n) { void g(), h(), i(); switch (n) { case 1: case 2: g(); [[fallthrough]]; case 3: // warning on fallthrough discouraged do { [[fallthrough]]; // error: next statement is not part of the same substatement execution } while (false); case 6: do { [[fallthrough]]; // error: next statement is not part of the same substatement execution } while (n--); case 7: while (false) { [[fallthrough]]; // error: next statement is not part of the same substatement execution } case 5: h(); case 4: // implementation may warn on fallthrough i(); [[fallthrough]]; // error } }— end example
void g(int); int f(int n) { if (n > 5) [[unlikely]] { // n > 5 is considered to be arbitrarily unlikely g(0); return n * 2 + 1; } switch (n) { case 1: g(1); [[fallthrough]]; [[likely]] case 2: // n == 2 is considered to be arbitrarily more g(2); // likely than any other value of n break; } return 3; }— end example
( string-literal )
struct [[nodiscard]] my_scopeguard { /* ... */ }; struct my_unique { my_unique() = default; // does not acquire resource [[nodiscard]] my_unique(int fd) { /* ... */ } // acquires resource ~my_unique() noexcept { /* ... */ } // releases resource, if any /* ... */ }; struct [[nodiscard]] error_info { /* ... */ }; error_info enable_missile_safety_mode(); void launch_missiles(); void test_missiles() { my_scopeguard(); // warning encouraged (void)my_scopeguard(), // warning not encouraged, cast to void launch_missiles(); // comma operator, statement continues my_unique(42); // warning encouraged my_unique(); // warning not encouraged enable_missile_safety_mode(); // warning encouraged launch_missiles(); } error_info &foo(); void f() { foo(); } // warning not encouraged: not a nodiscard call, because neither // the (reference) return type nor the function is declared nodiscard— end example
template<typename Key, typename Value, typename Hash, typename Pred, typename Allocator> class hash_map { [[no_unique_address]] Hash hasher; [[no_unique_address]] Pred pred; [[no_unique_address]] Allocator alloc; Bucket *buckets; // ... public: // ... };
module-declaration: export-keyword module-keyword module-name module-partition attribute-specifier-seq ;
module-name: module-name-qualifier identifier
module-partition: : module-name-qualifier identifier
module-name-qualifier: identifier . module-name-qualifier identifier .
Translation unit #1:
export module A; export import :Foo; export int baz();
Translation unit #2:
export module A:Foo; import :Internals; export int foo() { return 2 * (bar() + 1); }
Translation unit #3:
module A:Internals; int bar();
Translation unit #4:
module A; import :Internals; int bar() { return baz() - 10; } int baz() { return 30; }
Translation unit #1:
module B:Y; // does not implicitly import B int y();
Translation unit #2:
export module B; import :Y; // OK, does not create interface dependency cycle int n = y();
Translation unit #3:
module B:X1; // does not implicitly import B int &a = n; // error: n not visible here
Translation unit #4:
module B:X2; // does not implicitly import B import B; int &b = n; // OK
Translation unit #5:
module B; // implicitly imports B int &c = n; // OK— end example
export-declaration: export declaration export { declaration-seq } export-keyword module-import-declaration
Source file "a.h":
export int x;
Translation unit #1:
module; #include "a.h" // error: declaration of x is not in the // purview of a module interface unit export module M; export namespace {} // error: does not introduce any names export namespace { int a1; // error: export of name with internal linkage } namespace { export int a2; // error: export of name with internal linkage } export static int b; // error: b explicitly declared static export int f(); // OK export namespace N { } // OK export using namespace N; // error: does not declare a name— end example
Source file "b.h":
int f();
Importable header "c.h":
int g();
Translation unit #1:
export module X; export int h();
Translation unit #2:
module; #include "b.h" export module M; import "c.h"; import X; export using ::f, ::g, ::h; // OK struct S; export using ::S; // error: S has module linkage namespace N { export int h(); static int h(int); // #1 } export using N::h; // error: #1 has internal linkage— end example
export module M; struct S; export using T = S; // OK, exports name T denoting type S— end example
export module M; struct S { int n; }; typedef S S; export typedef S S; // OK, does not redeclare an entity export struct S; // error: exported declaration follows non-exported declaration— end example
Interface unit of M:
export module M; export struct X { static void f(); struct Y { }; }; namespace { struct S { }; } export void f(S); // OK struct T { }; export T id(T); // OK export struct A; // A exported as incomplete export auto rootFinder(double a) { return [=](double x) { return (x + a/x)/2; }; } export const int n = 5; // OK, n has external linkage
Implementation unit of M:
module M; struct A { int value; };
Main program:
import M; int main() { X::f(); // OK, X is exported and definition of X is reachable X::Y y; // OK, X::Y is exported as a complete type auto f = rootFinder(2); // OK return A{45}.value; // error: A is incomplete }— end example
Interface unit of M:
export module M; static int f(); // #1 export int f(); // error: #1 gives internal linkage struct S; // #2 export struct S; // error: #2 gives module linkage namespace { namespace N { extern int x; // #3 } } export int N::x; // error: #3 gives internal linkage— end example
export module M; export namespace N { int x; // OK static_assert(1 == 1); // error: does not declare a name }— end example
module-import-declaration: import-keyword module-name attribute-specifier-seq ; import-keyword module-partition attribute-specifier-seq ; import-keyword header-name attribute-specifier-seq ;
Translation unit #1:
module M:Part;
Translation unit #2:
export module M; export import :Part; // error: exported partition :Part is an implementation unit— end example
module M; import M; // error: cannot import M in its own unit— end example
Interface unit of M1:
export module M1; import M2;
Interface unit of M2:
export module M2; import M3;
Interface unit of M3:
export module M3; import M1; // error: cyclic interface dependency— end example
global-module-fragment: module-keyword ; declaration-seq
postfix-expression ( expression-list )whose postfix-expression denotes a dependent name, or for an operator expression whose operator denotes a dependent name, and D is found by name lookup for the corresponding name in an expression synthesized from E by replacing each type-dependent argument or operand with a value of a placeholder type with no associated namespaces or entities, or
const int size = 2; int ary1[size]; // unspecified whether size is decl-reachable from ary1 constexpr int identity(int x) { return x; } int ary2[identity(2)]; // unspecified whether identity is decl-reachable from ary2 template<typename> struct S; template<typename, int> struct S2; constexpr int g(int); template<typename T, int N> S<S2<T, g(N)>> f(); // S, S2, g, and :: are decl-reachable from f template<int N> void h() noexcept(g(N) == N); // g and :: are decl-reachable from h— end example
Source file "foo.h":
namespace N { struct X {}; int d(); int e(); inline int f(X, int = d()) { return e(); } int g(X); int h(X); }
Module M interface:
module; #include "foo.h" export module M; template<typename T> int use_f() { N::X x; // N::X, N, and :: are decl-reachable from use_f return f(x, 123); // N::f is decl-reachable from use_f, // N::e is indirectly decl-reachable from use_f // because it is decl-reachable from N::f, and // N::d is decl-reachable from use_f // because it is decl-reachable from N::f // even though it is not used in this call } template<typename T> int use_g() { N::X x; // N::X, N, and :: are decl-reachable from use_g return g((T(), x)); // N::g is not decl-reachable from use_g } template<typename T> int use_h() { N::X x; // N::X, N, and :: are decl-reachable from use_h return h((T(), x)); // N::h is not decl-reachable from use_h, but // N::h is decl-reachable from use_h<int> } int k = use_h<int>(); // use_h<int> is decl-reachable from k, so // N::h is decl-reachable from k
Module M implementation:
module M; int a = use_f<int>(); // OK int b = use_g<int>(); // error: no viable function for call to g; // g is not decl-reachable from purview of // module M's interface, so is discarded int c = use_h<int>(); // OK— end example
private-module-fragment: module-keyword : private ; declaration-seq
export module A; export inline void fn_e(); // error: exported inline function fn_e not defined // before private module fragment inline void fn_m(); // OK, module-linkage inline function static void fn_s(); export struct X; export void g(X *x) { fn_s(); // OK, call to static function in same translation unit fn_m(); // OK, call to module-linkage inline function } export X *factory(); // OK module :private; struct X {}; // definition not reachable from importers of A X *factory() { return new X (); } void fn_e() {} void fn_m() {} void fn_s() {}— end example
Translation unit #1:
export module stuff; export template<typename T, typename U> void foo(T, U u) { auto v = u; } export template<typename T, typename U> void bar(T, U u) { auto v = *u; }
Translation unit #2:
export module M1; import "defn.h"; // provides struct X {}; import stuff; export template<typename T> void f(T t) { X x; foo(t, x); }
Translation unit #3:
export module M2; import "decl.h"; // provides struct X; (not a definition) import stuff; export template<typename T> void g(T t) { X *x; bar(t, x); }
Translation unit #4:
import M1; import M2; void test() { f(0); g(0); }
Translation unit #1:
export module M:A; export struct B;
Translation unit #2:
module M:B; struct B { operator int(); };
Translation unit #3:
module M:C; import :A; B b1; // error: no reachable definition of struct B
Translation unit #4:
export module M; export import :A; import :B; B b2; export void f(B b = B());
Translation unit #5:
module X; import M; B b3; // error: no reachable definition of struct B void g() { f(); } // error: no reachable definition of struct B— end example
Translation unit #1:
export module A; struct X {}; export using Y = X;
Translation unit #2:
module B; import A; Y y; // OK, definition of X is reachable X x; // error: X not visible to unqualified lookup— end example
class-name: identifier simple-template-id
class-specifier: class-head { member-specification }
class-head: class-key attribute-specifier-seq class-head-name class-virt-specifier base-clause class-key attribute-specifier-seq base-clause
class-head-name: nested-name-specifier class-name
class-virt-specifier: final
class-key: class struct union
struct A; struct A final {}; // OK: definition of struct A, // not value-initialization of variable final struct X { struct C { constexpr operator int() { return 5; } }; struct B final : C{}; // OK: definition of nested class B, // not declaration of a bit-field member final };— end example
struct B { int i; }; // standard-layout class struct C : B { }; // standard-layout class struct D : C { }; // standard-layout class struct E : D { char : 4; }; // not a standard-layout class struct Q {}; struct S : Q { }; struct T : Q { }; struct U : S, T { }; // not a standard-layout class— end example
struct N { // neither trivial nor standard-layout int i; int j; virtual ~N(); }; struct T { // trivial but not standard-layout int i; private: int j; }; struct SL { // standard-layout but not trivial int i; int j; ~SL(); }; struct POD { // both trivial and standard-layout int i; int j; };— end example
struct X { int a; }; struct Y { int a; }; X a1; Y a2; int a3;declares three variables of three different types.
a1 = a2; // error: Y assigned to X a1 = a3; // error: int assigned to Xare type mismatches, and that
int f(X); int f(Y);declare an overloaded function f() and not simply a single function f() twice.
struct S { int a; }; struct S { int a; }; // error: double definitionis ill-formed because it defines S twice.
struct stat { // ... }; stat gstat; // use plain stat to define variable int stat(struct stat*); // redeclare stat as function void f() { struct stat* ps; // struct prefix needed to name struct stat stat(ps); // call stat() }— end example
struct s { int a; }; void g() { struct s; // hide global struct s with a block-scope declaration s* p; // refer to local struct s struct s { char* p; }; // define local struct s struct s; // redeclaration, has no effect }— end example
struct s { int a; }; void g(int s) { struct s* p = new struct s; // global s p->a = s; // parameter s }— end example
class A * A;first specifies A to be the name of a class and then redefines it as the name of a pointer to an object of that class.
member-specification: member-declaration member-specification access-specifier : member-specification
member-declaration: attribute-specifier-seq decl-specifier-seq member-declarator-list ; function-definition using-declaration using-enum-declaration static_assert-declaration template-declaration explicit-specialization deduction-guide alias-declaration opaque-enum-declaration empty-declaration
member-declarator-list: member-declarator member-declarator-list , member-declarator
member-declarator: declarator virt-specifier-seq pure-specifier declarator requires-clause declarator brace-or-equal-initializer identifier attribute-specifier-seq : constant-expression brace-or-equal-initializer
virt-specifier-seq: virt-specifier virt-specifier-seq virt-specifier
virt-specifier: override final
pure-specifier: = 0
struct S { using T = void(); T * p = 0; // OK: brace-or-equal-initializer virtual T f = 0; // OK: pure-specifier };— end example
int a; const int b = 0; struct S { int x1 : 8 = 42; // OK, "= 42" is brace-or-equal-initializer int x2 : 8 { 42 }; // OK, "{ 42 }" is brace-or-equal-initializer int y1 : true ? 8 : a = 42; // OK, brace-or-equal-initializer is absent int y2 : true ? 8 : b = 42; // error: cannot assign to const int int y3 : (true ? 8 : b) = 42; // OK, "= 42" is brace-or-equal-initializer int z : 1 || new int { 0 }; // OK, brace-or-equal-initializer is absent };— end example
struct tnode { char tword[20]; int count; tnode* left; tnode* right; };which contains an array of twenty characters, an integer, and two pointers to objects of the same type.
tnode s, *sp;
declares s to be a tnode and sp to be a pointer
to a tnode.struct A { int a; char b; }; struct B { const int b1; volatile char b2; }; struct C { int c; unsigned : 0; char b; }; struct D { int d; char b : 4; }; struct E { unsigned int e; char b; };— end example
struct T1 { int a, b; }; struct T2 { int c; double d; }; union U { T1 t1; T2 t2; }; int f() { U u = { { 1, 2 } }; // active member is t1 return u.t2.c; // OK, as if u.t1.a were nominated }— end example
struct X { typedef int T; static T count; void f(T); }; void X::f(T t = count) { }
typedef void fv(); typedef void fvc() const; struct S { fv memfunc1; // equivalent to: void memfunc1(); void memfunc2(); fvc memfunc3; // equivalent to: void memfunc3() const; }; fv S::* pmfv1 = &S::memfunc1; fv S::* pmfv2 = &S::memfunc2; fvc S::* pmfv3 = &S::memfunc3;
struct tnode { char tword[20]; int count; tnode* left; tnode* right; void set(const char*, tnode* l, tnode* r); }; void tnode::set(const char* w, tnode* l, tnode* r) { count = strlen(w)+1; if (sizeof(tword)<=count) perror("tnode string too long"); strcpy(tword,w); left = l; right = r; } void f(tnode n1, tnode n2) { n1.set("abc",&n2,0); n2.set("def",0,0); }
struct s { int a; int f() const; int g() { return a++; } int h() const { return a++; } // error }; int s::f() const { return a; }
struct A { }; // implicitly declared A::operator= struct B : A { B& operator=(const B &); }; B& B::operator=(const B& s) { this->A::operator=(s); // well-formed return *this; }— end example
ptr-declarator ( parameter-declaration-clause ) noexcept-specifier attribute-specifier-seqwhere the ptr-declarator consists solely of an id-expression, an optional attribute-specifier-seq, and optional surrounding parentheses, and the id-expression has one of the following forms:
struct S { S(); // declares the constructor }; S::S() { } // defines the constructor— end example
complex zz = complex(1,2.3); cprint( complex(7.8,1.2) );— end example
struct X { X(); // default constructor X(X&); // copy constructor with a non-const parameter }; const X cx; X x = cx; // error: X::X(X&) cannot copy cx into x— end example
struct S { template<typename T> S(T); S(); }; S g; void h() { S a(g); // does not instantiate the member template to produce S::S<S>(S); // uses the implicitly declared copy constructor }— end example
X::X(const X&)if each potentially constructed subobject of a class type M (or array thereof) has a copy constructor whose first parameter is of type const M& or const volatile M&.103
X::X(X&)
struct X { X(); X& operator=(X&); }; const X cx; X x; void f() { x = cx; // error: X::operator=(X&) cannot assign cx into x }— end example
X& X::operator=(const X&)if
X& X::operator=(X&)
struct S { int a; S& operator=(const S&) = default; };will not have a default move assignment operator implicitly declared because the copy assignment operator has been user-declared.
struct S { int a; S& operator=(const S&) = default; S& operator=(S&&) = default; };— end example
X& X::operator=(X&&)
ptr-declarator ( parameter-declaration-clause ) noexcept-specifier attribute-specifier-seqwhere the ptr-declarator consists solely of an id-expression, an optional attribute-specifier-seq, and optional surrounding parentheses, and the id-expression has one of the following forms:
struct B { virtual ~B() { } }; struct D : B { ~D() { } }; D D_object; typedef B B_alias; B* B_ptr = &D_object; void f() { D_object.B::~B(); // calls B's destructor B_ptr->~B(); // calls D's destructor B_ptr->~B_alias(); // calls D's destructor B_ptr->B_alias::~B(); // calls B's destructor B_ptr->B_alias::~B_alias(); // calls B's destructor }— end example
void* operator new(std::size_t, void* p) { return p; } struct X { X(int); ~X(); }; void f(X* p); void g() { // rare, specialized use: char* buf = new char[sizeof(X)]; X* p = new(buf) X(222); // use buf[] and initialize f(p); p->X::~X(); // cleanup }
typedef int I; I* p; p->I::~I();
struct X { operator int(); }; struct Y { operator X(); }; Y a; int b = a; // error: no viable conversion (a.operator X().operator int() not considered) int c = X(a); // OK: a.operator X().operator int()— end example
struct X { operator int(); }; struct Y : X { operator char(); }; void f(Y& a) { if (a) { // error: ambiguous between X::operator int() and Y::operator char() } }— end example
struct Z { explicit Z(); explicit Z(int); explicit Z(int, int); }; Z a; // OK: default-initialization performed Z b{}; // OK: direct initialization syntax used Z c = {}; // error: copy-list-initialization Z a1 = 1; // error: no implicit conversion Z a3 = Z(1); // OK: direct initialization syntax used Z a2(1); // OK: direct initialization syntax used Z* p = new Z(1); // OK: direct initialization syntax used Z a4 = (Z)1; // OK: explicit cast used Z a5 = static_cast<Z>(1); // OK: explicit cast used Z a6 = { 3, 4 }; // error: no implicit conversion— end example
conversion-function-id: operator conversion-type-id
conversion-type-id: type-specifier-seq conversion-declarator
conversion-declarator: ptr-operator conversion-declaratorspecifies a conversion from X to the type specified by the conversion-type-id.
class Y { }; struct Z { explicit operator Y() const; }; void h(Z z) { Y y1(z); // OK: direct-initialization Y y2 = z; // error: no conversion function candidate for copy-initialization Y y3 = (Y)z; // OK: cast notation } void g(X a, X b) { int i = (a) ? 1+a : 0; int j = (a&&b) ? a+b : i; if (a) { } }— end example
&ac.operator int*i; // syntax error: // parsed as: &(ac.operator int *)i // not as: &(ac.operator int)*i
operator int [[noreturn]] (); // error: noreturn attribute applied to a type— end example
struct S { operator auto() const { return 10; } // OK template<class T> operator auto() const { return 1.2; } // error: conversion function template };— end example
struct process { static void reschedule(); }; process& g(); void f() { process::reschedule(); // OK: no object necessary g().reschedule(); // g() is called }— end example
int g(); struct X { static int g(); }; struct Y : X { static int i; }; int Y::i = g(); // equivalent to Y::g();— end example
class process { static process* run_chain; static process* running; }; process* process::running = get_main(); process* process::run_chain = running;
identifier attribute-specifier-seq : constant-expression brace-or-equal-initializerspecifies a bit-field.
enum BOOL { FALSE=0, TRUE=1 }; struct A { BOOL b:1; }; A a; void f() { a.b = TRUE; if (a.b == TRUE) // yields true { /* ... */ } }— end example
int x; int y; struct enclose { int x; static int s; struct inner { void f(int i) { int a = sizeof(x); // OK: operand of sizeof is an unevaluated operand x = i; // error: assign to enclose::x s = i; // OK: assign to enclose::s ::x = i; // OK: assign to global x y = i; // OK: assign to global y } void g(enclose* p, int i) { p->x = i; // OK: assign to enclose::x } }; }; inner* p = 0; // error: inner not in scope— end example
struct enclose { struct inner { static int x; void f(int i); }; }; int enclose::inner::x = 1; void enclose::inner::f(int i) { /* ... */ }— end example
class E { class I1; // forward declaration of nested class class I2; class I1 { }; // definition of nested class }; class E::I2 { }; // definition of nested class— end example
struct X { typedef int I; class Y { /* ... */ }; I a; }; I b; // error Y c; // error X::Y d; // OK X::I e; // OK— end example
union U { int i; float f; std::string s; };
union A { int x; int y[4]; }; struct B { A a; }; union C { B b; int k; }; int f() { C c; // does not start lifetime of any union member c.b.a.y[3] = 4; // OK: S(c.b.a.y[3]) contains c.b and c.b.a.y; // creates objects to hold union members c.b and c.b.a.y return c.b.a.y[3]; // OK: c.b.a.y refers to newly created object (see [basic.life]) } struct X { const int a; int b; }; union Y { X x; int k; }; void g() { Y y = { { 1, 2 } }; // OK, y.x is active union member ([class.mem]) int n = y.x.a; y.k = 4; // OK: ends lifetime of y.x, y.k is active member of union y.x.b = n; // undefined behavior: y.x.b modified outside its lifetime, // S(y.x.b) is empty because X's default constructor is deleted, // so union member y.x's lifetime does not implicitly start }— end example
u.m.~M(); new (&u.n) N;— end example
union { member-specification } ;is called an anonymous union; it defines an unnamed type and an unnamed object of that type called an anonymous union object.
void f() { union { int aa; char* p; } obj, *ptr = &obj; aa = 1; // error ptr->aa = 1; // OK }
union U { int x = 0; union { int k; }; union { int z; int y = 1; // error: initialization for second variant member of U }; };— end example
int x; void f() { static int s; int x; const int N = 5; extern int q(); int arr[2]; auto [y, z] = arr; struct local { int g() { return x; } // error: odr-use of non-odr-usable variable x int h() { return s; } // OK int k() { return ::x; } // OK int l() { return q(); } // OK int m() { return N; } // OK: not an odr-use int* n() { return &N; } // error: odr-use of non-odr-usable variable N int p() { return y; } // error: odr-use of non-odr-usable structured binding y }; } local* p = 0; // error: local not in scope— end example
base-clause: : base-specifier-list
base-specifier-list: base-specifier ... base-specifier-list , base-specifier ...
base-specifier: attribute-specifier-seq class-or-decltype attribute-specifier-seq virtual access-specifier class-or-decltype attribute-specifier-seq access-specifier virtual class-or-decltype
class-or-decltype: nested-name-specifier type-name nested-name-specifier template simple-template-id decltype-specifier
access-specifier: private protected public
class A { /* ... */ }; class B { /* ... */ }; class C { /* ... */ }; class D : public A, public B, public C { /* ... */ };— end example
class X { /* ... */ }; class Y : public X, public X { /* ... */ }; // error
class L { public: int next; /* ... */ }; class A : public L { /* ... */ }; class B : public L { /* ... */ }; class C : public A, public B { void f(); /* ... */ }; // well-formed class D : public A, public L { void f(); /* ... */ }; // well-formed— end example
void C::f() { A::next = B::next; } // well-formed
class V { /* ... */ }; class A : virtual public V { /* ... */ }; class B : virtual public V { /* ... */ }; class C : public A, public B { /* ... */ };
class B { /* ... */ }; class X : virtual public B { /* ... */ }; class Y : virtual public B { /* ... */ }; class Z : public B { /* ... */ }; class AA : public X, public Y, public Z { /* ... */ };
struct A { virtual void f(); }; struct B : virtual A { virtual void f(); }; struct C : B , virtual A { using A::f; }; void foo() { C c; c.f(); // calls B::f, the final overrider c.C::f(); // calls A::f because of the using-declaration }— end example
struct A { virtual void f(); }; struct B : A { }; struct C : A { void f(); }; struct D : B, C { }; // OK: A::f and C::f are the final overriders // for the B and C subobjects, respectively— end example
struct B { virtual void f(); }; struct D : B { void f(int); }; struct D2 : D { void f(); };the function f(int) in class D hides the virtual function f() in its base class B; D::f(int) is not a virtual function.
struct B { virtual void f() const final; }; struct D : B { void f() const; // error: D::f attempts to override final B::f };— end example
struct B { virtual void f(int); }; struct D : B { virtual void f(long) override; // error: wrong signature overriding B::f virtual void f(int) override; // OK };— end example
struct A { virtual void f() requires true; // error: virtual function cannot be constrained ([temp.constr.decl]) };— end example
class B { }; class D : private B { friend class Derived; }; struct Base { virtual void vf1(); virtual void vf2(); virtual void vf3(); virtual B* vf4(); virtual B* vf5(); void f(); }; struct No_good : public Base { D* vf4(); // error: B (base class of D) inaccessible }; class A; struct Derived : public Base { void vf1(); // virtual and overrides Base::vf1() void vf2(int); // not virtual, hides Base::vf2() char vf3(); // error: invalid difference in return type only D* vf4(); // OK: returns pointer to derived class A* vf5(); // error: returns pointer to incomplete class void f(); }; void g() { Derived d; Base* bp = &d; // standard conversion: // Derived* to Base* bp->vf1(); // calls Derived::vf1() bp->vf2(); // calls Base::vf2() bp->f(); // calls Base::f() (not virtual) B* p = bp->vf4(); // calls Derived::vf4() and converts the // result to B* Derived* dp = &d; D* q = dp->vf4(); // calls Derived::vf4() and does not // convert the result to B* dp->vf2(); // error: argument mismatch }— end example
struct A { virtual void f(); }; struct B1 : A { // note non-virtual derivation void f(); }; struct B2 : A { void f(); }; struct D : B1, B2 { // D has two separate A subobjects }; void foo() { D d; // A* ap = &d; // would be ill-formed: ambiguous B1* b1p = &d; A* ap = b1p; D* dp = &d; ap->f(); // calls D::B1::f dp->f(); // error: ambiguous }
struct A { virtual void f(); }; struct VB1 : virtual A { // note virtual derivation void f(); }; struct VB2 : virtual A { void f(); }; struct Error : VB1, VB2 { // error }; struct Okay : VB1, VB2 { void f(); };
class point { /* ... */ }; class shape { // abstract class point center; public: point where() { return center; } void move(point p) { center=p; draw(); } virtual void rotate(int) = 0; // pure virtual virtual void draw() = 0; // pure virtual };— end example
struct C { virtual void f() = 0 { }; // error };— end example
class ab_circle : public shape { int radius; public: void rotate(int) { } // ab_circle::draw() is a pure virtual };
class circle : public shape { int radius; public: void rotate(int) { } void draw(); // a definition is required somewhere };would make class circle non-abstract and a definition of circle::draw() must be provided.
struct A { int x; }; // S(x,A) = { { A::x }, { A } } struct B { float x; }; // S(x,B) = { { B::x }, { B } } struct C: public A, public B { }; // S(x,C) = { invalid, { A in C, B in C } } struct D: public virtual C { }; // S(x,D) = S(x,C) struct E: public virtual C { char x; }; // S(x,E) = { { E::x }, { E } } struct F: public D, public E { }; // S(x,F) = S(x,E) int main() { F f; f.x = 0; // OK, lookup finds E::x }
struct A { int f(); };
struct B { int f(); };
struct C : A, B { int f() { return A::f() + B::f(); } };— end example
struct V { int v; }; struct A { int a; static int s; enum { e }; }; struct B : A, virtual V { }; struct C : A, virtual V { }; struct D : B, C { }; void f(D* pd) { pd->v++; // OK: only one v (virtual) pd->s++; // OK: only one s (static) int i = pd->e; // OK: only one e (enumerator) pd->a++; // error: ambiguous: two as in D }— end example
struct V { int f(); int x; }; struct W { int g(); int y; }; struct B : virtual V, W { int f(); int x; int g(); int y; }; struct C : virtual V, W { }; struct D : B, C { void glorp(); };
void D::glorp() { x++; // OK: B::x hides V::x f(); // OK: B::f() hides V::f() y++; // error: B::y and C's W::y g(); // error: B::g() and C's W::g() }— end example
struct V { }; struct A { }; struct B : A, virtual V { }; struct C : A, virtual V { }; struct D : B, C { }; void g() { D d; B* pb = &d; A* pa = &d; // error: ambiguous: C's A or B's A? V* pv = &d; // OK: only one V subobject }— end example
struct B1 { void f(); static void f(int); int i; }; struct B2 { void f(double); }; struct I1: B1 { }; struct I2: B1 { }; struct D: I1, I2, B2 { using B1::f; using B2::f; void g() { f(); // Ambiguous conversion of this f(0); // Unambiguous (static) f(0.0); // Unambiguous (only one B2) int B1::* mpB1 = &D::i; // Unambiguous int D::* mpD = &D::i; // Ambiguous conversion } };— end example
class X { int a; // X::a is private by default }; struct S { int a; // S::a is public by default };— end example
class A { class B { }; public: typedef B BB; }; void f() { A::BB x; // OK, typedef name A::BB is public A::B y; // access error, A::B is private }— end note
class A { typedef int I; // private member I f(); friend I g(I); static I x; template<int> struct Q; template<int> friend struct R; protected: struct B { }; }; A::I A::f() { return 0; } A::I g(A::I p = A::x); A::I g(A::I p) { return 0; } A::I A::x = 0; template<A::I> struct A::Q { }; template<A::I> struct R { }; struct D: A::B, A { };
class B { }; template <class T> class C { protected: typedef T TT; }; template <class U, class V = typename U::TT> class D : public U { }; D <C<B> >* d; // access error, C::TT is protected— end example
access-specifier : member-specification
class X { int a; // X::a is private by default: class used public: int b; // X::b is public int c; // X::c is public };— end example
struct S { int a; // S::a is public by default: struct used protected: int b; // S::b is protected private: int c; // S::c is private public: int d; // S::d is public };— end example
struct S { class A; enum E : int; private: class A { }; // error: cannot change access enum E: int { e0 }; // error: cannot change access };— end example
class B { /* ... */ }; class D1 : private B { /* ... */ }; class D2 : public B { /* ... */ }; class D3 : B { /* ... */ }; // B private by default struct D4 : public B { /* ... */ }; struct D5 : private B { /* ... */ }; struct D6 : B { /* ... */ }; // B public by default class D7 : protected B { /* ... */ }; struct D8 : protected B { /* ... */ };
class B { public: int mi; // non-static member static int si; // static member }; class D : private B { }; class DD : public D { void f(); }; void DD::f() { mi = 3; // error: mi is private in D si = 3; // error: si is private in D ::B b; b.mi = 3; // OK (b.mi is different from this->mi) b.si = 3; // OK (b.si is different from this->si) ::B::si = 3; // OK ::B* bp1 = this; // error: B is a private base class ::B* bp2 = (::B*)this; // OK with cast bp2->mi = 3; // OK: access through a pointer to B. }— end note
class B { public: int m; }; class S: private B { friend class N; }; class N: private S { void f() { B* p = this; // OK because class S satisfies the fourth condition above: B is a base class of N // accessible in f() because B is an accessible base class of S and S is an accessible // base class of N. } };— end example
class B; class A { private: int i; friend void f(B*); }; class B : public A { }; void f(B* p) { p->i = 1; // OK: B* can be implicitly converted to A*, and f has access to i in A }— end example
class X { int a; friend void friend_set(X*, int); public: void member_set(int); }; void friend_set(X* p, int i) { p->a = i; } void X::member_set(int i) { a = i; } void f() { X obj; friend_set(&obj,10); obj.member_set(10); }
class A { class B { }; friend class X; }; struct X : A::B { // OK: A::B accessible to friend A::B mx; // OK: A::B accessible to member of friend class Y { A::B my; // OK: A::B accessible to nested member of friend }; };— end example
class X { enum { a=100 }; friend class Y; }; class Y { int v[X::a]; // OK, Y is a friend of X }; class Z { int v[X::a]; // error: X::a is private };— end example
class A { friend class B { }; // error: cannot define class in friend declaration };— end example
friend elaborated-type-specifier ; friend simple-type-specifier ; friend typename-specifier ;
class C; typedef C Ct; class X1 { friend C; // OK: class C is a friend }; class X2 { friend Ct; // OK: class C is a friend friend D; // error: no type-name D in scope friend class D; // OK: elaborated-type-specifier declares new class }; template <typename T> class R { friend T; }; R<C> rc; // class C is a friend of R<C> R<int> Ri; // OK: "friend int;" is ignored— end example
class Y { friend char* X::foo(int); friend X::X(char); // constructors can be friends friend X::~X(); // destructors can be friends };— end example
class M { friend void f() { } // definition of global f, a friend of M, // not the definition of a member function };— end example
class A { friend class B; int a; }; class B { friend class C; }; class C { void f(A* p) { p->a++; // error: C is not a friend of A despite being a friend of a friend } }; class D : public B { void f(A* p) { p->a++; // error: D is not a friend of A despite being derived from a friend } };— end example
class X; void a(); void f() { class Y; extern void b(); class A { friend class X; // OK, but X is a local class, not ::X friend class Y; // OK friend class Z; // OK, introduces local class Z friend void a(); // error, ::a is not considered friend void b(); // OK friend void c(); // error }; X* px; // OK, but ::X is found Z* pz; // error: no Z is found }— end example
class B { protected: int i; static int j; }; class D1 : public B { }; class D2 : public B { friend void fr(B*,D1*,D2*); void mem(B*,D1*); }; void fr(B* pb, D1* p1, D2* p2) { pb->i = 1; // error p1->i = 2; // error p2->i = 3; // OK (access through a D2) p2->B::i = 4; // OK (access through a D2, even though naming class is B) int B::* pmi_B = &B::i; // error int B::* pmi_B2 = &D2::i; // OK (type of &D2::i is int B::*) B::j = 5; // error: not a friend of naming class B D2::j = 6; // OK (because refers to static member) } void D2::mem(B* pb, D1* p1) { pb->i = 1; // error p1->i = 2; // error i = 3; // OK (access through this) B::i = 4; // OK (access through this, qualification ignored) int B::* pmi_B = &B::i; // error int B::* pmi_B2 = &D2::i; // OK j = 5; // OK (because j refers to static member) B::j = 6; // OK (because B::j refers to static member) } void g(B* pb, D1* p1, D2* p2) { pb->i = 1; // error p1->i = 2; // error p2->i = 3; // error }— end example
class B { public: virtual int f(); }; class D : public B { private: int f(); }; void f() { D d; B* pb = &d; D* pd = &d; pb->f(); // OK: B::f() is public, D::f() is invoked pd->f(); // error: D::f() is private }— end example
class E { int x; class B { }; class I { B b; // OK: E::I can access E::B int y; void f(E* p, int i) { p->x = i; // OK: E::I can access E::x } }; int g(I* p) { return p->y; // error: I::y is private } };— end example
struct complex { complex(); complex(double); complex(double,double); }; complex sqrt(complex,complex); complex a(1); // initialized by calling complex(double) with argument 1 complex b = a; // initialized as a copy of a complex c = complex(1,2); // initialized by calling complex(double,double) with arguments 1 and 2 complex d = sqrt(b,c); // initialized by calling sqrt(complex,complex) with d as its result object complex e; // initialized by calling complex() complex f = 3; // initialized by calling complex(double) with argument 3 complex g = { 1, 2 }; // initialized by calling complex(double, double) with arguments 1 and 2— end example
complex v[6] = { 1, complex(1,2), complex(), 2 };
struct X { int i; float f; complex c; } x = { 99, 88.8, 77.7 };
ctor-initializer: : mem-initializer-list
mem-initializer-list: mem-initializer ... mem-initializer-list , mem-initializer ...
mem-initializer: mem-initializer-id ( expression-list ) mem-initializer-id braced-init-list
mem-initializer-id: class-or-decltype identifier
struct A { A(); }; typedef A global_A; struct B { }; struct C: public A, public B { C(); }; C::C(): global_A() { } // mem-initializer for base A— end example
struct A { A(); }; struct B: public virtual A { }; struct C: public A, public B { C(); }; C::C(): A() { } // error: which A?— end example
struct C { C( int ) { } // #1: non-delegating constructor C(): C(42) { } // #2: delegates to #1 C( char c ) : C(42.0) { } // #3: ill-formed due to recursion with #4 C( double d ) : C('a') { } // #4: ill-formed due to recursion with #3 };— end example
struct B1 { B1(int); /* ... */ }; struct B2 { B2(int); /* ... */ }; struct D : B1, B2 { D(int); B1 b; const int c; }; D::D(int a) : B2(a+1), B1(a+2), c(a+3), b(a+4) { /* ... */ } D d(10);— end example
struct A { A() : v(42) { } // error const int& v; };— end example
struct A { A(); }; struct B { B(int); }; struct C { C() { } // initializes members as follows: A a; // OK: calls A::A() const B b; // error: B has no default constructor int i; // OK: i has indeterminate value int j = 5; // OK: j has the value 5 };— end example
struct A { A() = default; // OK A(int v) : v(v) { } // OK const int& v = 42; // OK }; A a1; // error: ill-formed binding of temporary to reference A a2(1); // OK, unfortunately— end example
struct V { V(); V(int); }; struct A : virtual V { A(); A(int); }; struct B : virtual V { B(); B(int); }; struct C : A, B, virtual V { C(); C(int); }; A::A(int i) : V(i) { /* ... */ } B::B(int i) { /* ... */ } C::C(int i) { /* ... */ } V v(1); // use V(int) A a(2); // use V(int) B b(3); // use V() C c(4); // use V()— end example
class X { int a; int b; int i; int j; public: const int& r; X(int i): r(a), b(i), i(i), j(this->i) { } };
class A { public: A(int); }; class B : public A { int j; public: int f(); B() : A(f()), // undefined behavior: calls member function but base A not yet initialized j(f()) { } // well-defined: bases are all initialized }; class C { public: C(int); }; class D : public B, C { int i; public: D() : C(f()), // undefined behavior: calls member function but base C not yet initialized i(f()) { } // well-defined: bases are all initialized };— end example
template<class... Mixins> class X : public Mixins... { public: X(const Mixins&... mixins) : Mixins(mixins)... { } };— end example
struct B1 { B1(int, ...) { } }; struct B2 { B2(double) { } }; int get(); struct D1 : B1 { using B1::B1; // inherits B1(int, ...) int x; int y = get(); }; void test() { D1 d(2, 3, 4); // OK: B1 is initialized by calling B1(2, 3, 4), // then d.x is default-initialized (no initialization is performed), // then d.y is initialized by calling get() D1 e; // error: D1 has a deleted default constructor } struct D2 : B2 { using B2::B2; B1 b; }; D2 f(1.0); // error: B1 has a deleted default constructor struct W { W(int); }; struct X : virtual W { using W::W; X() = delete; }; struct Y : X { using X::X; }; struct Z : Y, virtual W { using Y::Y; }; Z z(0); // OK: initialization of Y does not invoke default constructor of X template<class T> struct Log : T { using T::T; // inherits all constructors from class T ~Log() { std::clog << "Destroying wrapper" << std::endl; } };
struct A { A(int); }; struct B : A { using A::A; }; struct C1 : B { using B::B; }; struct C2 : B { using B::B; }; struct D1 : C1, C2 { using C1::C1; using C2::C2; }; struct V1 : virtual B { using B::B; }; struct V2 : virtual B { using B::B; }; struct D2 : V1, V2 { using V1::V1; using V2::V2; }; D1 d1(0); // error: ambiguous D2 d2(0); // OK: initializes virtual B base class, which initializes the A base class // then initializes the V1 and V2 base classes as if by a defaulted default constructor struct M { M(); M(int); }; struct N : M { using M::M; }; struct O : M {}; struct P : N, O { using N::N; using O::O; }; P p(0); // OK: use M(0) to initialize N's base class, // use M() to initialize O's base class— end example
struct X { int i; }; struct Y : X { Y(); }; // non-trivial struct A { int a; }; struct B : public A { int j; Y y; }; // non-trivial extern B bobj; B* pb = &bobj; // OK int* p1 = &bobj.a; // undefined behavior: refers to base class member int* p2 = &bobj.y.i; // undefined behavior: refers to member's member A* pa = &bobj; // undefined behavior: upcast to a base class type B bobj; // definition of bobj extern X xobj; int* p3 = &xobj.i; // OK, X is a trivial class X xobj;
struct W { int j; }; struct X : public virtual W { }; struct Y { int* p; X x; Y() : p(&x.j) { // undefined, x is not yet constructed } };
struct C; void no_opt(C*); struct C { int c; C() : c(0) { no_opt(this); } }; const C cobj; void no_opt(C* cptr) { int i = cobj.c * 100; // value of cobj.c is unspecified cptr->c = 1; cout << cobj.c * 100 // value of cobj.c is unspecified << '\n'; } extern struct D d; struct D { D(int a) : a(a), b(d.a) {} int a, b; }; D d = D(1); // value of d.b is unspecified— end example
struct A { }; struct B : virtual A { }; struct C : B { }; struct D : virtual A { D(A*); }; struct X { X(A*); }; struct E : C, D, X { E() : D(this), // undefined behavior: upcast from E* to A* might use path E* → D* → A* // but D is not constructed // “D((C*)this)” would be defined: E* → C* is defined because E() has started, // and C* → A* is defined because C is fully constructed X(this) {} // defined: upon construction of X, C/B/D/A sublattice is fully constructed };— end example
struct V { virtual void f(); virtual void g(); }; struct A : virtual V { virtual void f(); }; struct B : virtual V { virtual void g(); B(V*, A*); }; struct D : A, B { virtual void f(); virtual void g(); D() : B((A*)this, this) { } }; B::B(V* v, A* a) { f(); // calls V::f, not A::f g(); // calls B::g, not D::g v->g(); // v is base of B, the call is well-defined, calls B::g a->f(); // undefined behavior: a's type not a base of B }— end example
struct V { virtual void f(); }; struct A : virtual V { }; struct B : virtual V { B(V*, A*); }; struct D : A, B { D() : B((A*)this, this) { } }; B::B(V* v, A* a) { typeid(*this); // type_info for B typeid(*v); // well-defined: *v has type V, a base of B yields type_info for B typeid(*a); // undefined behavior: type A not a base of B dynamic_cast<B*>(v); // well-defined: v of type V*, V base of B results in B* dynamic_cast<B*>(a); // undefined behavior: a has type A*, A not a base of B }— end example
class Thing { public: Thing(); ~Thing(); Thing(const Thing&); }; Thing f() { Thing t; return t; } Thing t2 = f(); struct A { void *p; constexpr A(): p(this) {} }; constexpr A g() { A loc; return loc; } constexpr A a; // well-formed, a.p points to a constexpr A b = g(); // error: b.p would be dangling ([expr.const]) void h() { A c = g(); // well-formed, c.p may point to c or to an ephemeral temporary }
class Thing { public: Thing(); ~Thing(); Thing(Thing&&); private: Thing(const Thing&); }; Thing f(bool b) { Thing t; if (b) throw t; // OK: Thing(Thing&&) used (or elided) to throw t return t; // OK: Thing(Thing&&) used (or elided) to return t } Thing t2 = f(false); // OK: no extra copy/move performed, t2 constructed by call to f struct Weird { Weird(); Weird(Weird&); }; Weird g() { Weird w; return w; // OK: first overload resolution fails, second overload resolution selects Weird(Weird&) }— end example
template<class T> void g(const T&); template<class T> void f() { T x; try { T y; try { g(x); } catch (...) { if (/*...*/) throw x; // does not move throw y; // moves } g(y); } catch(...) { g(x); g(y); // error: y is not in scope } }— end example
template<typename T> struct X { friend constexpr std::partial_ordering operator<=>(X, X) requires (sizeof(T) != 1) = default; // implicitly declares: friend constexpr bool operator==(X, X) requires (sizeof(T) != 1) = default; [[nodiscard]] virtual std::strong_ordering operator<=>(const X&) const = default; // implicitly declares: [[nodiscard]] virtual bool operator==(const X&) const = default; };— end example
a == b ? strong_ordering::equal : a < b ? strong_ordering::less : strong_ordering::greater
a == b ? weak_ordering::equivalent : a < b ? weak_ordering::less : weak_ordering::greater
a == b ? partial_ordering::equivalent : a < b ? partial_ordering::less : b < a ? partial_ordering::greater : partial_ordering::unordered
class Arena; struct B { void* operator new(std::size_t, Arena*); }; struct D1 : B { }; Arena* ap; void foo(int i) { new (ap) D1; // calls B::operator new(std::size_t, Arena*) new D1[i]; // calls ::operator new[](std::size_t) new D1; // error: ::operator new(std::size_t) hidden }— end example
class X { void operator delete(void*); void operator delete[](void*, std::size_t); }; class Y { void operator delete(void*, std::size_t); void operator delete[](void*); };— end example
struct B { virtual ~B(); void operator delete(void*, std::size_t); }; struct D : B { void operator delete(void*); }; struct E : B { void log_deletion(); void operator delete(E *p, std::destroying_delete_t) { p->log_deletion(); p->~E(); ::operator delete(p); } }; void f() { B* bp = new D; delete bp; // 1: uses D::operator delete(void*) bp = new E; delete bp; // 2: uses E::operator delete(E*, std::destroying_delete_t) }
struct B { virtual ~B(); void operator delete[](void*, std::size_t); }; struct D : B { void operator delete[](void*, std::size_t); }; void f(int i) { D* dp = new D[i]; delete [] dp; // uses D::operator delete[](void*, std::size_t) B* bp = new D[i]; delete[] bp; // undefined behavior }
class X { static void f(); void f(); // error void f() const; // error void f() const volatile; // error void g(); void g() const; // OK: no static g void g() const volatile; // OK: no static g };— end example
class Y { void h() &; void h() const &; // OK void h() &&; // OK, all declarations have a ref-qualifier void i() &; void i() const; // error: prior declaration of i has a ref-qualifier };— end example
typedef int Int; void f(int i); void f(Int i); // OK: redeclaration of f(int) void f(int i) { /* ... */ } void f(Int i) { /* ... */ } // error: redefinition of f(int)— end example
enum E { a }; void f(int i) { /* ... */ } void f(E i) { /* ... */ }— end example
int f(char*); int f(char[]); // same as f(char*); int f(char[7]); // same as f(char*); int f(char[9]); // same as f(char*); int g(char(*)[10]); int g(char[5][10]); // same as g(char(*)[10]); int g(char[7][10]); // same as g(char(*)[10]); int g(char(*)[20]); // different from g(char(*)[10]);— end example
void h(int()); void h(int (*)()); // redeclaration of h(int()) void h(int x()) { } // definition of h(int()) void h(int (*x)()) { } // error: redefinition of h(int())— end example
typedef const int cInt; int f (int); int f (const int); // redeclaration of f(int) int f (int) { /* ... */ } // definition of f(int) int f (cInt) { /* ... */ } // error: redefinition of f(int)— end example
void f (int i, int j); void f (int i, int j = 99); // OK: redeclaration of f(int, int) void f (int i = 88, int j); // OK: redeclaration of f(int, int) void f (); // OK: overloaded declaration of f void prog () { f (1, 2); // OK: call f(int, int) f (1); // OK: call f(int, int) f (); // error: f(int, int) or f()? }— end example
template<int I> concept C = true; template<typename T> struct A { void f() requires C<42>; // #1 void f() requires true; // OK, different functions };— end example
void f(const char*); void g() { extern void f(int); f("asdf"); // error: f(int) hides f(const char*) // so there is no f(const char*) in this scope } void caller () { extern void callee(int, int); { extern void callee(int); // hides callee(int, int) callee(88, 99); // error: only callee(int) in scope } }— end example
class T { public: T(); }; class C : T { public: C(int); }; T a = 1; // error: no viable conversion (T(C(1)) not considered)— end example
struct A { A(); // #1 A(A &&); // #2 template<typename T> A(T &&); // #3 }; struct B : A { using A::A; B(const B &); // #4 B(B &&) = default; // #5, implicitly deleted struct X { X(X &&) = delete; } x; }; extern B b1; B b2 = static_cast<B&&>(b1); // calls #4: #1 is not viable, #2, #3, and #5 are not candidates struct C { operator B&&(); }; B b3 = C(); // calls #4— end example
postfix-expression ( expression-list )if the postfix-expression names at least one function or function template, overload resolution is applied as specified in [over.call.func].
postfix-expression: postfix-expression . id-expression postfix-expression -> id-expression primary-expression
operator conversion-type-id ( ) cv-qualifier-seq ref-qualifier noexcept-specifier attribute-specifier-seq ;where the optional cv-qualifier-seq is the same cv-qualification as, or a greater cv-qualification than, cv, and where conversion-type-id denotes the type “pointer to function of () returning R”, or the type “reference to pointer to function of () returning R”, or the type “reference to function of () returning R”, a surrogate call function with the unique name call-function and having the form
R call-function ( conversion-type-id F, P a, …, P a) { return F (a, …, a); }is also considered as a candidate function.
int f1(int); int f2(float); typedef int (*fp1)(int); typedef int (*fp2)(float); struct A { operator fp1() { return f1; } operator fp2() { return f2; } } a; int i = a(1); // calls f1 via pointer returned from conversion function— end example
struct String { String (const String&); String (const char*); operator const char* (); }; String operator + (const String&, const String&); void f() { const char* p= "one" + "two"; // error: cannot add two pointers; overloaded operator+ not considered // because neither operand has class or enumeration type int I = 1 + 1; // always evaluates to 2 even if class or enumeration types exist // that would perform the operation. }— end example
Subclause | Expression | As member function | As non-member function |
@a | |||
a@b | |||
a=b | |||
a[b] | |||
a-> | |||
a@ |
struct X { operator double(); }; struct Y { operator int*(); }; int *a = Y() + 100.0; // error: pointer arithmetic requires integral operand int *b = Y() + X(); // error: pointer arithmetic requires integral operand— end example
struct A { }; void operator + (A, A); struct B { void operator + (B); void f (); }; A a; void B::f() { operator+ (a,a); // error: global operator hidden by member a + a; // OK: calls global operator+ }— end note
typename nested-name-specifier template simple-template-idas specified in [dcl.type.simple].
template <typename> class AA;with a single partial specialization whose template parameter list is that of A and whose template argument list is a specialization of A with the template argument list of A ([temp.dep.type]), AA<T> matches the partial specialization.
template <class T> struct A { explicit A(const T&, ...) noexcept; // #1 A(T&&, ...); // #2 }; int i; A a1 = { i, i }; // error: explicit constructor #1 selected in copy-list-initialization during deduction, // cannot deduce from non-forwarding rvalue reference in #2 A a2{i, i}; // OK, #1 deduces to A<int> and also initializes A a3{0, i}; // OK, #2 deduces to A<int> and also initializes A a4 = {0, i}; // OK, #2 deduces to A<int> and also initializes template <class T> A(const T&, const T&) -> A<T&>; // #3 template <class T> explicit A(T&&, T&&) -> A<T>; // #4 A a5 = {0, 1}; // error: explicit deduction guide #4 selected in copy-list-initialization during deduction A a6{0,1}; // OK, #4 deduces to A<int> and #2 initializes A a7 = {0, i}; // error: #3 deduces to A<int&>, #1 and #2 declare same constructor A a8{0,i}; // error: #3 deduces to A<int&>, #1 and #2 declare same constructor template <class T> struct B { template <class U> using TA = T; template <class U> B(U, TA<U>); }; B b{(int*)0, (char*)0}; // OK, deduces B<char*> template <typename T> struct S { T x; T y; }; template <typename T> struct C { S<T> s; T t; }; template <typename T> struct D { S<int> s; T t; }; C c1 = {1, 2}; // error: deduction failed C c2 = {1, 2, 3}; // error: deduction failed C c3 = {{1u, 2u}, 3}; // OK, deduces C<int> D d1 = {1, 2}; // error: deduction failed D d2 = {1, 2, 3}; // OK, braces elided, deduces D<int> template <typename T> struct E { T t; decltype(t) t2; }; E e1 = {1, 2}; // OK, deduces E<int> template <typename... T> struct Types {}; template <typename... T> struct F : Types<T...>, T... {}; struct X {}; struct Y {}; struct Z {}; struct W { operator Y(); }; F f1 = {Types<X, Y, Z>{}, {}, {}}; // OK, F<X, Y, Z> deduced F f2 = {Types<X, Y, Z>{}, X{}, Y{}}; // OK, F<X, Y, Z> deduced F f3 = {Types<X, Y, Z>{}, X{}, W{}}; // error: conflicting types deduced; operator Y not considered— end example
template <class T, class U> struct C { C(T, U); // #1 }; template<class T, class U> C(T, U) -> C<T, std::type_identity_t<U>>; // #2 template<class V> using A = C<V *, V *>; template<std::integral W> using B = A<W>; int i{}; double d{}; A a1(&i, &i); // deduces A<int> A a2(i, i); // error: cannot deduce V * from i A a3(&i, &d); // error: #1: cannot deduce (V*, V*) from (int *, double *) // #2: cannot deduce A<V> from C<int *, double *> B b1(&i, &i); // deduces B<int> B b2(&d, &d); // error: cannot deduce B<W> from C<double *, double *>
// The following concept ensures a specialization of A is deduced. template <class> class AA; template <class V> class AA<A<V>> { }; template <class T> concept deduces_A = requires { sizeof(AA<T>); }; // f1 is formed from the constructor #1 of C, generating the following function template template<T, U> auto f1(T, U) -> C<T, U>; // Deducing arguments for C<T, U> from C<V *, V*> deduces T as V * and U as V *; // f1' is obtained by transforming f1 as described by the above procedure. template<class V> requires deduces_A<C<V *, V *>> auto f1_prime(V *, V*) -> C<V *, V *>; // f2 is formed from the deduction-guide #2 of C template<class T, class U> auto f2(T, U) -> C<T, std::type_identity_t<U>>; // Deducing arguments for C<T, std::type_identity_t<U>> from C<V *, V*> deduces T as V *; // f2' is obtained by transforming f2 as described by the above procedure. template<class V, class U> requires deduces_A<C<V *, std::type_identity_t<U>>> auto f2_prime(V *, U) -> C<V *, std::type_identity_t<U>>; // The following concept ensures a specialization of B is deduced. template <class> class BB; template <class V> class BB<B<V>> { }; template <class T> concept deduces_B = requires { sizeof(BB<T>); }; // The guides for B derived from the above f1' and f2' for A are as follows: template<std::integral W> requires deduces_A<C<W *, W *>> && deduces_B<C<W *, W *>> auto f1_prime_for_B(W *, W *) -> C<W *, W *>; template<std::integral W, class U> requires deduces_A<C<W *, std::type_identity_t<U>>> && deduces_B<C<W *, std::type_identity_t<U>>> auto f2_prime_for_B(W *, U) -> C<W *, std::type_identity_t<U>>;
struct A { A(); operator int(); operator double(); } a; int i = a; // a.operator int() followed by no conversion is better than // a.operator double() followed by a conversion to int float x = a; // ambiguous: both possibilities require conversions, // and neither is better than the other— end example
template <class T> struct A { operator T&(); // #1 operator T&&(); // #2 }; typedef int Fn(); A<Fn> a; Fn& lf = a; // calls #1 Fn&& rf = a; // calls #2— end example
struct A { A(int = 0); }; struct B: A { using A::A; B(); }; int main() { B b; // OK, B::B() }— end example
struct S { friend auto operator<=>(const S&, const S&) = default; // #1 friend bool operator<(const S&, const S&); // #2 }; bool b = S() < S(); // calls #2— end example
struct S { friend std::weak_ordering operator<=>(const S&, int); // #1 friend std::weak_ordering operator<=>(int, const S&); // #2 }; bool b = 1 < S(); // calls #2— end example
template <class T> struct A { using value_type = T; A(value_type); // #1 A(const A&); // #2 A(T, T, int); // #3 template<class U> A(int, T, U); // #4 // #5 is the copy deduction candidate, A(A) }; A x(1, 2, 3); // uses #3, generated from a non-template constructor template <class T> A(T) -> A<T>; // #6, less specialized than #5 A a(42); // uses #6 to deduce A<int> and #1 to initialize A b = a; // uses #5 to deduce A<int> and #2 to initialize template <class T> A(A<T>) -> A<A<T>>; // #7, as specialized as #5 A b2 = a; // uses #7 to deduce A<A<int>> and #1 to initialize— end example
void Fcn(const int*, short); void Fcn(int*, int); int i; short s = 0; void f() { Fcn(&i, s); // is ambiguous because &i → int* is better than &i → const int* // but s → short is also better than s → int Fcn(&i, 1L); // calls Fcn(int*, int), because &i → int* is better than &i → const int* // and 1L → short and 1L → int are indistinguishable Fcn(&i, 'c'); // calls Fcn(int*, int), because &i → int* is better than &i → const int* // and c → int is better than c → short }— end example
namespace A { extern "C" void f(int = 5); } namespace B { extern "C" void f(int = 5); } using A::f; using B::f; void use() { f(3); // OK, default argument was not used for viability f(); // error: found default argument twice }— end example
class B; class A { A (B&);}; class B { operator A (); }; class C { C (B&); }; void f(A) { } void f(C) { } B b; f(b); // error: ambiguous because there is a conversion b → C (via constructor) // and an (ambiguous) conversion b → A (via constructor or conversion function) void f(B) { } f(b); // OK, unambiguous— end example
Conversion | Category | Rank | Subclause |
No conversions required | Identity | ||
Lvalue-to-rvalue conversion | |||
Array-to-pointer conversion | Lvalue Transformation | ||
Function-to-pointer conversion | Exact Match | ||
Qualification conversions | |||
Function pointer conversion | Qualification Adjustment | ||
Integral promotions | |||
Floating-point promotion | Promotion | Promotion | |
Integral conversions | |||
Floating-point conversions | |||
Floating-integral conversions | |||
Pointer conversions | Conversion | Conversion | |
Pointer-to-member conversions | |||
Boolean conversions |
struct A {}; struct B : public A {} b; int f(A&); int f(B&); int i = f(b); // calls f(B&), an exact match, rather than f(A&), a conversion— end example
struct A { int x, y; }; struct B { int y, x; }; void f(A a, int); // #1 void f(B b, ...); // #2 void g(A a); // #3 void g(B b); // #4 void h() { f({.x = 1, .y = 2}, 0); // OK; calls #1 f({.y = 2, .x = 1}, 0); // error: selects #1, initialization of a fails // due to non-matching member order ([dcl.init.list]) g({.x = 1, .y = 2}); // error: ambiguous between #3 and #4 }— end example
void f(std::initializer_list<int>); f( {} ); // OK: f(initializer_list<int>) identity conversion f( {1,2,3} ); // OK: f(initializer_list<int>) identity conversion f( {'a','b'} ); // OK: f(initializer_list<int>) integral promotion f( {1.0} ); // error: narrowing struct A { A(std::initializer_list<double>); // #1 A(std::initializer_list<complex<double>>); // #2 A(std::initializer_list<std::string>); // #3 }; A a{ 1.0,2.0 }; // OK, uses #1 void g(A); g({ "foo", "bar" }); // OK, uses #3 typedef int IA[3]; void h(const IA&); h({ 1, 2, 3 }); // OK: identity conversion— end example
struct A { A(std::initializer_list<int>); }; void f(A); f( {'a', 'b'} ); // OK: f(A(std::initializer_list<int>)) user-defined conversion struct B { B(int, double); }; void g(B); g( {'a', 'b'} ); // OK: g(B(int, double)) user-defined conversion g( {1.0, 1.0} ); // error: narrowing void f(B); f( {'a', 'b'} ); // error: ambiguous f(A) or f(B) struct C { C(std::string); }; void h(C); h({"foo"}); // OK: h(C(std::string("foo"))) struct D { D(A, C); }; void i(D); i({ {1,2}, {"bar"} }); // OK: i(D(A(std::initializer_list<int>{1,2}), C(std::string("bar"))))— end example
struct A { int m1; double m2; }; void f(A); f( {'a', 'b'} ); // OK: f(A(int,double)) user-defined conversion f( {1.0} ); // error: narrowing— end example
struct A { int m1; double m2; }; void f(const A&); f( {'a', 'b'} ); // OK: f(A(int,double)) user-defined conversion f( {1.0} ); // error: narrowing void g(const double &); g({1}); // same conversion as int to double— end example
void f(int); f( {'a'} ); // OK: same conversion as char to int f( {1.0} ); // error: narrowing— end example
void f(int); f( { } ); // OK: identity conversion— end example
void f1(int); // #1 void f1(std::initializer_list<long>); // #2 void g1() { f1({42}); } // chooses #2 void f2(std::pair<const char*, const char*>); // #3 void f2(std::initializer_list<std::string>); // #4 void g2() { f2({"foo","bar"}); } // chooses #4— end example
void f(int (&&)[] ); // #1 void f(double (&&)[] ); // #2 void f(int (&&)[2]); // #3 f( {1} ); // Calls #1: Better than #2 due to conversion, better than #3 due to bounds f( {1.0} ); // Calls #2: Identity conversion is better than floating-integral conversion f( {1.0, 2.0} ); // Calls #2: Identity conversion is better than floating-integral conversion f( {1, 2} ); // Calls #3: Converting to array of known bound is better than to unknown bound, // and an identity conversion is better than floating-integral conversion— end example
int i; int f1(); int&& f2(); int g(const int&); int g(const int&&); int j = g(i); // calls g(const int&) int k = g(f1()); // calls g(const int&&) int l = g(f2()); // calls g(const int&&) struct A { A& operator<<(int); void p() &; void p() &&; }; A& operator<<(A&&, char); A() << 1; // calls A::operator<<(int) A() << 'c'; // calls operator<<(A&&, char) A a; a << 1; // calls A::operator<<(int) a << 'c'; // calls A::operator<<(int) A().p(); // calls A::p()&& a.p(); // calls A::p()&— end example
int f(void(&)()); // #1 int f(void(&&)()); // #2 void g(); int i1 = f(g); // calls #1— end example
int f(const volatile int *); int f(const int *); int i; int j = f(&i); // calls f(const int*)— end example
int f(const int &); int f(int &); int g(const int &); int g(int); int i; int j = f(i); // calls f(int &) int k = g(i); // ambiguous struct X { void f() const; void f(); }; void g(const X& a, X b) { a.f(); // calls X::f() const b.f(); // calls X::f() }— end example
struct A { operator short(); } a; int f(int); int f(float); int i = f(a); // calls f(int), because short → int is // better than short → float.— end example
struct A {}; struct B : public A {}; struct C : public B {}; C* pc; int f(A*); int f(B*); int i = f(pc); // calls f(B*)— end example
int f(double); int f(int); int (*pfd)(double) = &f; // selects f(double) int (*pfi)(int) = &f; // selects f(int) int (*pfe)(...) = &f; // error: type mismatch int (&rfi)(int) = f; // selects f(int) int (&rfd)(double) = f; // selects f(double) void g() { (int (*)(int))&f; // cast expression as selector }
struct X { int f(int); static int f(long); }; int (X::*p1)(int) = &X::f; // OK int (*p2)(int) = &X::f; // error: mismatch int (*p3)(long) = &X::f; // OK int (X::*p4)(long) = &X::f; // error: mismatch int (X::*p5)(int) = &(X::f); // error: wrong syntax for // pointer to member int (*p6)(long) = &(X::f); // OK— end example
operator-function-id: operator operator
operator: one of new delete new[] delete[] co_await ( ) [ ] -> ->* ~ ! + - * / % ^ & | = += -= *= /= %= ^= &= |= == != < > <= >= <=> && || << >> <<= >>= ++ -- ,
. .* :: ?:
nor can the preprocessing symbols
# ([cpp.stringize])
and
## ([cpp.concat]).complex z = a.operator+(b); // complex z = a+b; void* p = operator new(sizeof(int)*n);— end example
cast-expression . operator @ ()Otherwise, if a non-member function is selected, the expression is interpreted as
operator @ ( cast-expression )
struct B { virtual int operator= (int); virtual B& operator= (const B&); }; struct D : B { virtual int operator= (int); virtual D& operator= (const B&); }; D dobj1; D dobj2; B* bptr = &dobj1; void f() { bptr->operator=(99); // calls D::operator=(int) *bptr = 99; // ditto bptr->operator=(dobj2); // calls D::operator=(const B&) *bptr = dobj2; // ditto dobj1 = dobj2; // calls implicitly-declared D::operator=(const D&) }— end example
postfix-expression ( expression-list )where the postfix-expression is of class type, the operator function is selected by overload resolution ([over.call.object]).
postfix-expression . operator conversion-type-id () ( expression-list )Otherwise, the expression is interpreted as
postfix-expression . operator () ( expression-list )
postfix-expression [ expr-or-braced-init-list ]the operator function is selected by overload resolution ([over.match.oper]).
postfix-expression . operator [] ( expr-or-braced-init-list )
postfix-expression -> template id-expressionthe operator function is selected by overload resolution ([over.match.oper]), and the expression is interpreted as
( postfix-expression . operator -> () ) -> template id-expression
struct X { X& operator++(); // prefix ++a X operator++(int); // postfix a++ }; struct Y { }; Y& operator++(Y&); // prefix ++b Y operator++(Y&, int); // postfix b++ void f(X a, Y b) { ++a; // a.operator++(); a++; // a.operator++(0); ++b; // operator++(b); b++; // operator++(b, 0); a.operator++(); // explicit call: like ++a; a.operator++(0); // explicit call: like a++; operator++(b); // explicit call: like ++b; operator++(b, 0); // explicit call: like b++; }— end example
vq T& operator++(vq T&); T operator++(vq T&, int);
vq T& operator--(vq T&); T operator--(vq T&, int);
T*vq& operator++(T*vq&); T*vq& operator--(T*vq&); T* operator++(T*vq&, int); T* operator--(T*vq&, int);
T& operator*(T*);
T& operator*(T*);
T operator+(T); T operator-(T);
T operator~(T);
cv12 T& operator->*(cv1 C1*, cv2 T C2::*);where cv12 is the union of cv1 and cv2.
LR operator*(L, R); LR operator/(L, R); LR operator+(L, R); LR operator-(L, R); bool operator==(L, R); bool operator!=(L, R); bool operator<(L, R); bool operator>(L, R); bool operator<=(L, R); bool operator>=(L, R);where LR is the result of the usual arithmetic conversions ([expr.arith.conv]) between types L and R.
std::strong_ordering operator<=>(T, T);
std::partial_ordering operator<=>(L, R);
T* operator+(T*, std::ptrdiff_t); T& operator[](T*, std::ptrdiff_t); T* operator-(T*, std::ptrdiff_t); T* operator+(std::ptrdiff_t, T*); T& operator[](std::ptrdiff_t, T*);
std::ptrdiff_t operator-(T, T);
bool operator==(T, T); bool operator!=(T, T); bool operator<(T, T); bool operator>(T, T); bool operator<=(T, T); bool operator>=(T, T); R operator<=>(T, T);where R is the result type specified in [expr.spaceship].
bool operator==(T, T); bool operator!=(T, T);
LR operator%(L, R); LR operator&(L, R); LR operator^(L, R); LR operator|(L, R); L operator<<(L, R); L operator>>(L, R);where LR is the result of the usual arithmetic conversions ([expr.arith.conv]) between types L and R.
vq L& operator=(vq L&, R); vq L& operator*=(vq L&, R); vq L& operator/=(vq L&, R); vq L& operator+=(vq L&, R); vq L& operator-=(vq L&, R);
T*vq& operator=(T*vq&, T*);
vq T& operator=(vq T&, T);
T*vq& operator+=(T*vq&, std::ptrdiff_t); T*vq& operator-=(T*vq&, std::ptrdiff_t);
vq L& operator%=(vq L&, R); vq L& operator<<=(vq L&, R); vq L& operator>>=(vq L&, R); vq L& operator&=(vq L&, R); vq L& operator^=(vq L&, R); vq L& operator|=(vq L&, R);
bool operator!(bool); bool operator&&(bool, bool); bool operator||(bool, bool);
LR operator?:(bool, L, R);where LR is the result of the usual arithmetic conversions ([expr.arith.conv]) between types L and R.
literal-operator-id: operator string-literal identifier operator user-defined-string-literal
const char* unsigned long long int long double char wchar_t char8_t char16_t char32_t const char*, std::size_t const wchar_t*, std::size_t const char8_t*, std::size_t const char16_t*, std::size_t const char32_t*, std::size_t
void operator "" _km(long double); // OK string operator "" _i18n(const char*, std::size_t); // OK template <char...> double operator "" _\u03C0(); // OK: UCN for lowercase pi float operator ""_e(const char*); // OK float operator ""E(const char*); // error: reserved literal suffix ([usrlit.suffix], [lex.ext]) double operator""_Bq(long double); // OK: does not use the reserved identifier _Bq ([lex.name]) double operator"" _Bq(long double); // uses the reserved identifier _Bq ([lex.name]) float operator " " B(const char*); // error: non-empty string-literal string operator "" 5X(const char*, std::size_t); // error: invalid literal suffix identifier double operator "" _miles(double); // error: invalid parameter-declaration-clause template <char...> int operator "" _j(const char*); // error: invalid parameter-declaration-clause extern "C" void operator "" _m(long double); // error: C language linkage— end example
template-declaration: template-head declaration template-head concept-definition
template-head: template < template-parameter-list > requires-clause
template-parameter-list: template-parameter template-parameter-list , template-parameter
requires-clause: requires constraint-logical-or-expression
constraint-logical-or-expression: constraint-logical-and-expression constraint-logical-or-expression || constraint-logical-and-expression
constraint-logical-and-expression: primary-expression constraint-logical-and-expression && primary-expression
template<class T> constexpr T pi = T(3.1415926535897932385L); template<class T> T circular_area(T r) { return pi<T> * r * r; } struct matrix_constants { template<class T> using pauli = hermitian_matrix<T, 2>; template<class T> constexpr static pauli<T> sigma1 = { { 0, 1 }, { 1, 0 } }; template<class T> constexpr static pauli<T> sigma2 = { { 0, -1i }, { 1i, 0 } }; template<class T> constexpr static pauli<T> sigma3 = { { 1, 0 }, { 0, -1 } }; };— end example
template<int N> requires N == sizeof new unsigned short int f(); // error: parentheses required around == expression— end example