A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://timsong-cpp.github.io/cppwp/n4140/expr.prim below:

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

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

class Outer {
  int a[sizeof(*this)];                 unsigned int sz = sizeof(*this);    
  void f() {
    int b[sizeof(*this)];             
    struct Inner {
      int c[sizeof(*this)];               };
  }
};

 — 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.

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.

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

5.1.2 Lambda expressions [expr.prim.lambda]

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 neither an aggregate ([dcl.init.aggr]) nor a literal type ([basic.types]). 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:

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

The closure type for a non-generic lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function with C++ language linkage ([dcl.link]) 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. For a generic lambda with no lambda-capture, the closure type has a public non-virtual non-explicit const conversion function template to pointer to function. The conversion function template has the same invented template-parameter-list, and the pointer to function has the same parameter types, as the function call operator template. The return type of the pointer to function shall behave as if it were a decltype-specifier denoting the return type of the corresponding function call operator template specialization. [ Note: If the generic lambda has no trailing-return-type or the trailing-return-type contains a placeholder type, return type deduction of the corresponding function call operator template specialization has to be done. The corresponding specialization is that instantiation of the function call operator template with the same template arguments as those deduced for the conversion function template. Consider the following:

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) {
            ...
  }
  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; }
};

 — end note ] [ Example:

void f1(int (*)(int))   { }
void f2(char (*)(int))  { }

void g(int (*)(int))    { }  void g(char (*)(char))  { }  
void h(int (*)(int))    { }  void h(char (*)(int))   { }  
auto glambda = [](auto a) { return a; };
f1(glambda);  f2(glambda);  g(glambda);   h(glambda);   int& (*fpi)(int*) = [](auto* a) -> auto& { return *a; }; 

 — end example ] The value returned by any given specialization of this conversion function template shall be the address of a function that, when invoked, has the same effect as invoking the generic lambda's corresponding function call operator template specialization. [ Note: This will result in the implicit instantiation of the generic lambda's body. The instantiated generic lambda's return type and parameter types shall match the return type and parameter types of the pointer to function.  — end note ] [ Example:

auto GL = [](auto a) { std::cout << a; return a; };
int (*GL_int)(int) = GL;  GL_int(3);                

 — end example ]

An init-capture behaves as if it declares and explicitly captures a variable of the form “auto init-capture ;” whose declarative region is the lambda-expression's compound-statement, except that:

Note: This enables an init-capture like “x = std::move(x)”; the second “x” must bind to a declaration in the surrounding context.  — end note ] [ Example:

int x = 4;
auto y = [&r = x, x = x+1]()->int {
            r += 2;
            return x+2;
         }();  

 — end example ]

A lambda-expression with an associated capture-default that does not explicitly capture this or a variable with automatic storage duration (this excludes any id-expression that has been found to refer to an init-capture's associated non-static data member), is said to implicitly capture the entity (i.e., this or a variable) if the compound-statement:

Example:

void f(int, const int (&)[2] = {})    { }   void f(const int&, const int (&)[1])  { }   void test() {
  const int x = 17;
  auto g = [](auto a) {
    f(x);    };

  auto g2 = [=](auto a) {
    int selector[sizeof(a) == 1 ? 1 : 2]{};
    f(x, selector);    };
}

 — end example ] All such implicitly captured 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 or an instantiation of the function call operator template of a generic lambda 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];                    x[0][0] = i;                                                  };
  };
  struct s1 {
    int f;
    void work(int n) {
      int m = n*n;
      int j = 40;
      auto m3 = [this,m] {
        auto m4 = [&,j] {                 int x = n;                                                      x += m;                                                         x += i;                         x += f;                                                       };
      };
    }
  };
}

 — 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; })());          void g2(int = ([i]{ return 0; })());          void g3(int = ([=]{ return i; })());          void g4(int = ([=]{ return 0; })());          void g5(int = ([]{ return sizeof i; })());  }

 — 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 is not of the form & identifier or & identifier initializer. 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 ] A member of an anonymous union shall not be captured by copy.

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. A member of an anonymous union shall not be 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:

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 within the compound-statement of a lambda-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];                 f(&N);                                                };
}

 — 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;
  [=] {                         decltype(x) y1;             decltype((x)) y2 = y1;                                  decltype(r) r1 = y1;        decltype((r)) r2 = y2;    };
}

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

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, and the non-static data members corresponding to the init-captures are initialized as indicated by the corresponding initializer (which may be copy- or direct-initialization). (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 simple-capture followed by an ellipsis is a pack expansion ([temp.variadic]). An init-capture followed by an ellipsis is ill-formed. [ Example:

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

 — end example ]


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4