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());
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 }; } };
struct S { int m; }; int i = sizeof(S::m); // OK int j = sizeof(S::m + 42); // OK
unqualified-id: identifier operator-function-id conversion-function-id literal-operator-id ~ class-name ~ decltype-specifier template-id
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 ::
lambda-expression: lambda-introducer lambda-declarator compound-statement
lambda-introducer: [ lambda-capture ]
lambda-declarator: ( parameter-declaration-clause ) decl-specifier-seq noexcept-specifier attribute-specifier-seq trailing-return-type
#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); }); }
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&
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
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); // ill-formed
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()); // ill-formed: two() is not a constant expression static_assert(add(one)(one)() == monoid(2)()); // OK
auto glambda = [](auto a) { return a; }; int (*fp)(int) = glambda;The behavior of the conversion function of glambda above is like that of the following conversion function:
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
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)
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); // ill-formed
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* }; } };
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 [&, &i]{ }; // error: i preceded by & when & is the default [=, *this]{ }; // OK [=, this]{ }; // error: this when = is the default [i, i]{ }; // error: i repeated [this, *this]{ }; // error: this appears twice }
void f() {
int x = 0;
auto g = [x](int x) { return 0; } // error: parameter and simple-capture have the same name
}
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
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 g2 = [=](auto a) { int selector[sizeof(a) == 1 ? 1 : 2]{}; f(x, selector); // OK: is a dependent expression, so captures x }; }
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 captured by m3 int x = n; // error: n implicitly captured by m4 but not captured by m3 x += m; // OK: m implicitly captured by m4 and explicitly captured by m3 x += i; // error: i is outside of the reaching scope 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 }(); } };
void f2() { int i = 1; void g1(int = ([i]{ return i; })()); // ill-formed void g2(int = ([i]{ return 0; })()); // ill-formed void g3(int = ([=]{ return i; })()); // ill-formed void g4(int = ([=]{ return 0; })()); // OK void g5(int = ([]{ return sizeof i; })()); // OK }
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 }; } auto h(int &r) { return [&] { ++r; // Valid after h returns if the lifetime of the // object to which r is bound has not ended }; }
// 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);
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;
void f3() { float x, &r = x; [=] { // x and r are not captured (appearance in a decltype operand is not an odr-use) 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& (transformation not considered) decltype((r)) r2 = y2; // r2 has type float const& }; }
template<class... Args> void f(Args... args) { auto lm = [&, args...] { return g(args...); }; lm(); }
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 parameter packs }
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 . pseudo-destructor-name postfix-expression -> pseudo-destructor-name 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
pseudo-destructor-name: nested-name-specifier type-name :: ~ type-name nested-name-specifier template simple-template-id :: ~ type-name ~ type-name ~ decltype-specifier
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
}
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);
nested-name-specifier type-name :: ~ type-nameshall designate the same scalar type (ignoring cv-qualification).
struct B { };
struct D : B { };
void foo(D* dp) {
B* bp = dynamic_cast<B*>(dp); // equivalent to B* bp = dp;
}
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; // ill-formed: cast from virtual base E* ep1 = dynamic_cast<E*>(ap); // succeeds }
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
struct B { };
struct D : public B { };
D d;
B &br = d;
static_cast<D&>(br); // produces lvalue to the original d object
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 }
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.
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
unary-expression: postfix-expression ++ cast-expression -- cast-expression unary-operator cast-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
template<class... Types> struct count { static const std::size_t value = sizeof...(Types); };
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-listEntities created by a new-expression have dynamic storage duration ([basic.stc.dynamic]).
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>
new int(*[10])(); // error
(new int) (*[10])(); // error
Instead, the explicitly parenthesized version of the new
operator can be used to create objects of compound
types ([basic.compound]):new (int (*[10])());
void mergeable(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 unmergeable(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; } }
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; // ill-formed: non-placement deallocation function matches // placement allocation function
delete-expression: :: delete cast-expression :: delete [ ] cast-expressionThe first alternative is for non-array objects, and the second is for arrays.
noexcept-expression: noexcept ( 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
}
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; // ill-formed: cs is a const object }
additive-expression: multiplicative-expression additive-expression + multiplicative-expression additive-expression - multiplicative-expressionFor addition, either both operands shall have arithmetic or unscoped enumeration type, or one operand shall be a pointer to a completely-defined object type and the other shall have integral or unscoped enumeration type.
shift-expression: additive-expression shift-expression << additive-expression shift-expression >> additive-expressionThe operands shall be of integral or unscoped enumeration type and integral promotions are performed.
relational-expression: shift-expression relational-expression < shift-expression relational-expression > shift-expression relational-expression <= shift-expression relational-expression >= shift-expressionThe operands shall have arithmetic, enumeration, or pointer type.
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
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
conditional-expression: logical-or-expression logical-or-expression ? expression : assignment-expression
try { // ... } catch (...) { // catch all exceptions // respond (partially) to exception throw; // pass the exception to some other handler }
assignment-expression: conditional-expression logical-or-expression assignment-operator initializer-clause throw-expression
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
expression: assignment-expression expression , assignment-expressionA pair of expressions separated by a comma is evaluated left-to-right; the left expression is a discarded-value expression (Clause [expr]).
void g() { const int n = 0; [=] { constexpr int i = n; // OK, n is not odr-used and not captured here constexpr int j = *&n; // ill-formed, &n would be an odr-use of n }; }
auto monad = [](auto v) { return [=] { return v; }; };
auto bind = [](auto m) {
return [=](auto fvm) { return fvm(m()); };
};
// OK to have captures to automatic objects created during constant expression evaluation.
static_assert(bind(monad(2))(monad)() == monad(2)());
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)
struct A { constexpr A(int i) : val(i) { } constexpr operator int() const { return val; } constexpr operator long() const { return 43; } private: int val; }; template<int> struct X { }; constexpr A a = 42; X<a> x; // OK: unique conversion to int int ary[a]; // error: ambiguous conversion