5 Expressions [expr]

5.1 Primary expressions [expr.prim]

5.1.1 General [expr.prim.general]

primary-expression:
    literal
    this
    ( expression )
    id-expression
    lambda-expression
id-expression:
    unqualified-id
    qualified-id
unqualified-id:
    identifier
    operator-function-id
    conversion-function-id
    literal-operator-id
    ~ class-name
    ~ decltype-specifier
    template-id

A literal is a primary expression. Its type depends on its form ([lex.literal]). A string literal is an lvalue; all other literals are prvalues.

The keyword this names a pointer to the object for which a non-static member function ([class.this]) is invoked or a non-static data member's initializer ([class.mem]) is evaluated.

If a declaration declares a member function or member function template of a class X, the expression this is a prvalue of type “pointer to cv-qualifier-seq X” between the optional cv-qualifer-seq and the end of the function-definition, member-declarator, or declarator. It shall not appear before the optional cv-qualifier-seq and it shall not appear within the declaration of a static member function (although its type and value category are defined within a static member function as they are within a non-static member function). [ Note: this is because declaration matching does not occur until the complete declarator is known.  — end note ] Unlike the object expression in other contexts, *this is not required to be of complete type for purposes of class member access ([expr.ref]) outside the member function body. [ Note: only class members declared prior to the declaration are visible.  — end note ] [ Example:

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 ]

Otherwise, if a member-declarator declares a non-static data member ([class.mem]) of a class X, the expression this is a prvalue of type “pointer to X” within the optional brace-or-equal-initializer. It shall not appear elsewhere in the member-declarator.

The expression this shall not appear in any other context. [ Example:

class Outer {
  int a[sizeof(*this)];               // error: not inside a member function
  unsigned int sz = sizeof(*this);    // OK: in brace-or-equal-initializer

  void f() {
    int b[sizeof(*this)];             // OK

    struct Inner {
      int c[sizeof(*this)];           // error: not inside a member function of Inner
    };
  }
};

 — end example ]

A parenthesized expression is a primary expression whose type and value are identical to those of the enclosed expression. The presence of parentheses does not affect whether the expression is an lvalue. The parenthesized expression can be used in exactly the same contexts as those where the enclosed expression can be used, and with the same meaning, except as otherwise indicated.

An id-expression is a restricted form of a primary-expression. [ Note: an id-expression can appear after . and -> operators ([expr.ref]).  — end note ]

An identifier is an id-expression provided it has been suitably declared (Clause [dcl.dcl]). [ Note: for operator-function-ids, see [over.oper]; for conversion-function-ids, see [class.conv.fct]; for literal-operator-ids, see [over.literal]; for template-ids, see [temp.names]. A class-name or decltype-specifier prefixed by ~ denotes a destructor; see [class.dtor]. Within the definition of a non-static member function, an identifier that names a non-static member is transformed to a class member access expression ([class.mfct.non-static]).  — end note ] The type of the expression is the type of the identifier. The result is the entity denoted by the identifier. The result is an lvalue if the entity is a function, variable, or data member and a prvalue otherwise.

qualified-id:
    nested-name-specifier templateopt unqualified-id
    :: identifier
    :: operator-function-id
    :: literal-operator-id
    :: template-id

nested-name-specifier:
    ::opt type-name ::
    ::opt namespace-name ::
    decltype-specifier ::
    nested-name-specifier identifier ::
    nested-name-specifier templateopt simple-template-id ::

A nested-name-specifier that denotes a class, optionally followed by the keyword template ([temp.names]), and then followed by the name of a member of either that class ([class.mem]) or one of its base classes (Clause [class.derived]), is a qualified-id; [class.qual] describes name lookup for class members that appear in qualified-ids. The result is the member. The type of the result is the type of the member. The result is an lvalue if the member is a static member function or a data member and a prvalue otherwise. [ Note: a class member can be referred to using a qualified-id at any point in its potential scope ([basic.scope.class]).  — end note ] Where class-name :: class-name is used, and the two class-names refer to the same class, this notation names the constructor ([class.ctor]). Where class-name ::~ class-name is used, the two class-names shall refer to the same class; this notation names the destructor ([class.dtor]). The form ~ decltype-specifier also denotes the destructor, but it shall not be used as the unqualified-id in a qualified-id. [ Note: a typedef-name that names a class is a class-name ([class.name]).  — end note ]

A ::, or a nested-name-specifier that names a namespace ([basic.namespace]), in either case followed by the name of a member of that namespace (or the name of a member of a namespace made visible by a using-directive) is a qualified-id; [namespace.qual] describes name lookup for namespace members that appear in qualified-ids. The result is the member. The type of the result is the type of the member. The result is an lvalue if the member is a function or a variable and a prvalue otherwise.

A nested-name-specifier that denotes an enumeration ([dcl.enum]), followed by the name of an enumerator of that enumeration, is a qualified-id that refers to the enumerator. The result is the enumerator. The type of the result is the type of the enumeration. The result is a prvalue.

In a qualified-id, if the unqualified-id is a conversion-function-id, its conversion-type-id shall denote the same type in both the context in which the entire qualified-id occurs and in the context of the class denoted by the nested-name-specifier.

An id-expression that denotes a non-static data member or non-static member function of a class can only be used:

This also applies when the object expression is an implicit (*this) ([class.mfct.non-static]).

5.1.2 Lambda expressions [expr.prim.lambda]

Lambda expressions provide a concise way to create simple function objects. [ Example:

#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 ]

lambda-expression:
    lambda-introducer lambda-declaratoropt compound-statement
lambda-introducer:
    [ lambda-captureopt ]
lambda-capture:
    capture-default
    capture-list
    capture-default , capture-list
capture-default:
    &
    =
capture-list:
    capture ...opt
    capture-list , capture ...opt
capture:
    identifier
    & identifier
    this
lambda-declarator:
    ( parameter-declaration-clause ) mutableopt
    exception-specificationopt attribute-specifier-seqopt trailing-return-typeopt

The evaluation of a lambda-expression results in a prvalue temporary ([class.temporary]). This temporary is called the closure object. A lambda-expression shall not appear in an unevaluated operand (Clause [expr]). [ Note: A closure object behaves like a function object ([function.objects]). — end note ]

The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type — called the closure type — whose properties are described below. This class type is not an aggregate ([dcl.init.aggr]). The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression. [ Note: This determines the set of namespaces and classes associated with the closure type ([basic.lookup.argdep]). The parameter types of a lambda-declarator do not affect these associated namespaces and classes.  — end note ] An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:

  • the size and/or alignment of the closure type,

  • whether the closure type is trivially copyable (Clause [class]),

  • whether the closure type is a standard-layout class (Clause [class]), or

  • whether the closure type is a POD class (Clause [class]).

An implementation shall not add members of rvalue reference type to the closure type.

If a lambda-expression does not include a lambda-declarator, it is as if the lambda-declarator were (). If a lambda-expression does not include a trailing-return-type, it is as if the trailing-return-type denotes the following type:

Example:

auto x1 = [](int i){ return i; }; // OK: return type is int
auto x2 = []{ return { 1, 2 }; }; // error: the return type is void (a
                                  // braced-init-list is not an expression)

 — end example ]

The closure type for a lambda-expression has a public inline function call operator ([over.call]) whose parameters and return type are described by the lambda-expression's parameter-declaration-clause and trailing-return-type respectively. This function call operator is declared const ([class.mfct.non-static]) if and only if the lambda-expression's parameter-declaration-clause is not followed by mutable. It is neither virtual nor declared volatile. Default arguments ([dcl.fct.default]) shall not be specified in the parameter-declaration-clause of a lambda-declarator. Any exception-specification specified on a lambda-expression applies to the corresponding function call operator. An attribute-specifier-seq in a lambda-declarator appertains to the type of the corresponding function call operator. [ Note: Names referenced in the lambda-declarator are looked up in the context in which the lambda-expression appears.  — end note ]

The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type's function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type's function call operator.

The lambda-expression's compound-statement yields the function-body ([dcl.fct.def]) of the function call operator, but for purposes of name lookup ([basic.lookup]), determining the type and value of this ([class.this]) and transforming id-expressions referring to non-static class members into class member access expressions using (*this) ([class.mfct.non-static]), the compound-statement is considered in the context of the lambda-expression. [ 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 ]

If a lambda-capture includes a capture-default that is &, the identifiers in the lambda-capture shall not be preceded by &. If a lambda-capture includes a capture-default that is =, the lambda-capture shall not contain this and each identifier it contains shall be preceded by &. An identifier or this shall not appear more than once in a lambda-capture. [ Example:

struct S2 { void f(int i); };
void S2::f(int i) {
  [&, i]{ };    // OK
  [&, &i]{ };   // error: i preceded by & when & is the default
  [=, this]{ }; // error: this when = is the default
  [i, i]{ };    // error: i repeated
}

 — end example ]

A lambda-expression whose smallest enclosing scope is a block scope ([basic.scope.local]) is a local lambda expression; any other lambda-expression shall not have a capture-list in its lambda-introducer. The reaching scope of a local lambda expression is the set of enclosing scopes up to and including the innermost enclosing function and its parameters. [ Note: This reaching scope includes any intervening lambda-expressions.  — end note ]

The identifiers in a capture-list are looked up using the usual rules for unqualified name lookup ([basic.lookup.unqual]); each such lookup shall find a variable with automatic storage duration declared in the reaching scope of the local lambda expression. An entity (i.e. a variable or this) is said to be explicitly captured if it appears in the lambda-expression's capture-list.

If a lambda-expression has an associated capture-default and its compound-statement odr-uses ([basic.def.odr]) this or a variable with automatic storage duration and the odr-used entity is not explicitly captured, then the odr-used entity is said to be implicitly captured; such entities shall be declared within the reaching scope of the lambda expression. [ Note: The implicit capture of an entity by a nested lambda-expression can cause its implicit capture by the containing lambda-expression (see below). Implicit odr-uses of this can result in implicit capture.  — end note ]

An entity is captured if it is captured explicitly or implicitly. An entity captured by a lambda-expression is odr-used ([basic.def.odr]) in the scope containing the lambda-expression. If this is captured by a local lambda expression, its nearest enclosing function shall be a non-static member function. If a lambda-expression odr-uses ([basic.def.odr]) this or a variable with automatic storage duration from its reaching scope, that entity shall be captured by the lambda-expression. If a lambda-expression captures an entity and that entity is not defined or captured in the immediately enclosing lambda expression or function, the program is ill-formed. [ 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 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
        };
      };
    }
  };
}

 — end example ]

A lambda-expression appearing in a default argument shall not implicitly or explicitly capture any entity. [ Example:

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
}

 — end example ]

An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly captured with a capture that does not include an &. For each entity captured by copy, an unnamed non-static data member is declared in the closure type. The declaration order of these members is unspecified. The type of such a data member is the type of the corresponding captured entity if the entity is not a reference to an object, or the referenced type otherwise. [ Note: If the captured entity is a reference to a function, the corresponding data member is also a reference to a function.  — end note ]

An entity is captured by reference if it is implicitly or explicitly captured but not captured by copy. It is unspecified whether additional unnamed non-static data members are declared in the closure type for entities captured by reference.

If a lambda-expression m2 captures an entity and that entity is captured by an immediately enclosing lambda-expression m1, then m2's capture is transformed as follows:

  • if m1 captures the entity by copy, m2 captures the corresponding non-static data member of m1's closure type;

  • if m1 captures the entity by reference, m2 captures the same entity captured by m1.

Example: the nested lambda expressions and invocations below will output 123234.

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 ]

Every id-expression that is an odr-use ([basic.def.odr]) of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type. [ Note: An id-expression that is not an odr-use refers to the original entity, never to a member of the closure type. Furthermore, such an id-expression does not cause the implicit capture of the entity.  — end note ] If this is captured, each odr-use of this is transformed into an access to the corresponding unnamed data member of the closure type, cast ([expr.cast]) to the type of this. [ Note: The cast ensures that the transformed expression is a prvalue.  — end note ] [ 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 ]

Every occurrence of decltype((x)) where x is a possibly parenthesized id-expression that names an entity of automatic storage duration is treated as if x were transformed into an access to a corresponding data member of the closure type that would have been declared if x were an odr-use of the denoted entity. [ Example:

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&
  };
}

 — end example ]

The closure type associated with a lambda-expression has a deleted ([dcl.fct.def.delete]) default constructor and a deleted copy assignment operator. It has an implicitly-declared copy constructor ([class.copy]) and may have an implicitly-declared move constructor ([class.copy]). [ Note: The copy/move constructor is implicitly defined in the same way as any other implicitly declared copy/move constructor would be implicitly defined.  — end note ]

The closure type associated with a lambda-expression has an implicitly-declared destructor ([class.dtor]).

When the lambda-expression is evaluated, the entities that are captured by copy are used to direct-initialize each corresponding non-static data member of the resulting closure object. (For array members, the array elements are direct-initialized in increasing subscript order.) These initializations are performed in the (unspecified) order in which the non-static data members are declared. [ Note: This ensures that the destructions will occur in the reverse order of the constructions.  — end note ]

Note: If an entity is implicitly or explicitly captured by reference, invoking the function call operator of the corresponding lambda-expression after the lifetime of the entity has ended is likely to result in undefined behavior.  — end note ]

A capture followed by an ellipsis is a pack expansion ([temp.variadic]). [ Example:

template<class... Args>
void f(Args... args) {
  auto lm = [&, args...] { return g(args...); };
  lm();
}

 — end example ]