7 Expressions [expr]

7.5 Primary expressions [expr.prim]

7.5.5 Names [expr.prim.id]

7.5.5.1 General [expr.prim.id.general]

An id-expression is a restricted form of a primary-expression.
[Note 1:  — end note]
If an id-expression E denotes a non-static non-type member of some class C at a point where the current class ([expr.prim.this]) is X and the id-expression is transformed into a class member access expression using (*this) as the object expression.
[Note 2: 
If C is not X or a base class of X, the class member access expression is ill-formed.
Also, if the id-expression occurs within a static or explicit object member function, the class member access is ill-formed.
— end note]
This transformation does not apply in the template definition context ([temp.dep.type]).
If an id-expression E denotes a member M of an anonymous union ([class.union.anon]) U:
  • If U is a non-static data member, E refers to M as a member of the lookup context of the terminal name of E (after any implicit transformation to a class member access expression).
    [Example 1: 
    o.x is interpreted as o.u.x, where u names the anonymous union member.
    — end example]
  • Otherwise, E is interpreted as a class member access ([expr.ref]) that designates the member subobject M of the anonymous union variable for U.
    [Note 3: 
    Under this interpretation, E no longer denotes a non-static data member.
    — end note]
    [Example 2: 
    N​::​x is interpreted as N​::​u.x, where u names the anonymous union variable.
    — end example]
An id-expression that denotes a non-static data member or implicit object member function of a class can only be used:
  • as part of a class member access (after any implicit transformation (see above)) in which the object expression refers to the member's class or a class derived from that class, or
  • to form a pointer to member ([expr.unary.op]), or
  • if that id-expression denotes a non-static data member and it appears in an unevaluated operand.
    [Example 3: struct S { int m; }; int i = sizeof(S::m); // OK int j = sizeof(S::m + 42); // OK — end example]
For an id-expression that denotes an overload set, overload resolution is performed to select a unique function ([over.match], [over.over]).
[Note 4: 
A program cannot refer to a function with a trailing requires-clause whose constraint-expression is not satisfied, because such functions are never selected by overload resolution.
[Example 4: 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 }
In each case, the constraints of f are not satisfied.
In the declaration of p2, those constraints need to be satisfied even though f is an unevaluated operand.
— end example]
— end note]

7.5.5.2 Unqualified names [expr.prim.id.unqual]

An identifier is only an id-expression if it has been suitably declared ([dcl.dcl]) or if it appears as part of a declarator-id ([dcl.decl]).
An identifier that names a coroutine parameter refers to the copy of the parameter ([dcl.fct.def.coroutine]).
[Note 1: 
A type-name or computed-type-specifier prefixed by ~ denotes the destructor of the type so named; see [expr.prim.id.dtor].
— end note]
A component name of an unqualified-id U is
[Note 2: 
Other constructs that contain names to look up can have several component names ([expr.prim.id.qual], [dcl.type.simple], [dcl.type.elab], [dcl.mptr], [namespace.udecl], [temp.param], [temp.names], [temp.res]).
— end note]
The terminal name of a construct is the component name of that construct that appears lexically last.
The result is the entity denoted by the unqualified-id ([basic.lookup.unqual]).
If the unqualified-id appears in a lambda-expression at program point P and the entity is a local entity ([basic.pre]) or a variable declared by an init-capture ([expr.prim.lambda.capture]), then let S be the compound-statement of the innermost enclosing lambda-expression of P.
If naming the entity from outside of an unevaluated operand within S would refer to an entity captured by copy in some intervening lambda-expression, then let E be the innermost such lambda-expression.
  • If there is such a lambda-expression and if P is in E's function parameter scope but not its parameter-declaration-clause, then the type of the expression is the type of a class member access expression ([expr.ref]) naming the non-static data member that would be declared for such a capture in the object parameter ([dcl.fct]) of the function call operator of E.
    [Note 3: 
    If E is not declared mutable, the type of such an identifier will typically be const qualified.
    — end note]
  • Otherwise (if there is no such lambda-expression or if P either precedes E's function parameter scope or is in E's parameter-declaration-clause), the type of the expression is the type of the result.
If the entity is a template parameter object for a template parameter of type T ([temp.param]), the type of the expression is const T.
In all other cases, the type of the expression is the type of the entity.
[Note 4: 
The type will be adjusted as described in [expr.type] if it is cv-qualified or is a reference type.
— end note]
The expression is an xvalue if it is move-eligible (see below); an lvalue if the entity is a function, variable, structured binding, data member, or template parameter object; and a prvalue otherwise ([basic.lval]); it is a bit-field if the identifier designates a bit-field.
[Example 1: void f() { float x, &r = x; [=]() -> decltype((x)) { // lambda returns float const& because this lambda is not mutable and // x is an lvalue decltype(x) y1; // y1 has type float decltype((x)) y2 = y1; // y2 has type float const& decltype(r) r1 = y1; // r1 has type float& decltype((r)) r2 = y2; // r2 has type float const& return y2; }; [=](decltype((x)) y) { decltype((x)) z = x; // OK, y has type float&, z has type float const& }; [=] { [](decltype((x)) y) {}; // OK, lambda takes a parameter of type float const& [x=1](decltype((x)) y) { decltype((x)) z = x; // OK, y has type int&, z has type int const& }; }; } — end example]
An implicitly movable entity is a variable of automatic storage duration that is either a non-volatile object or an rvalue reference to a non-volatile object type.
In the following contexts, an id-expression is move-eligible:

7.5.5.3 Qualified names [expr.prim.id.qual]

The component names of a qualified-id are those of its nested-name-specifier and unqualified-id.
The component names of a nested-name-specifier are its identifier (if any) and those of its type-name, namespace-name, simple-template-id, and/or nested-name-specifier.
A declarative nested-name-specifier shall not have a computed-type-specifier.
A declaration that uses a declarative nested-name-specifier shall be a friend declaration or inhabit a scope that contains the entity being redeclared or specialized.
The nested-name-specifier ​::​ nominates the global namespace.
A nested-name-specifier with a computed-type-specifier nominates the type denoted by the computed-type-specifier, which shall be a class or enumeration type.
If a nested-name-specifier N is declarative and has a simple-template-id with a template argument list A that involves a template parameter, let T be the template nominated by N without A.
T shall be a class template.
Any other nested-name-specifier nominates the entity denoted by its type-name, namespace-name, identifier, or simple-template-id.
If the nested-name-specifier is not declarative, the entity shall not be a template.
A qualified-id shall not be of the form nested-name-specifier template ~ computed-type-specifier nor of the form computed-type-specifier ​::​ ~ type-name.
The result of a qualified-id Q is the entity it denotes ([basic.lookup.qual]).
The type of the expression is the type of the result.
The result is an lvalue if the member is
  • a function other than a non-static member function,
  • a non-static member function if Q is the operand of a unary & operator,
  • a variable,
  • a structured binding ([dcl.struct.bind]), or
  • a data member,
and a prvalue otherwise.

7.5.5.4 Pack indexing expression [expr.prim.pack.index]

The id-expression P in a pack-index-expression shall be an identifier that denotes a pack.
The constant-expression shall be a converted constant expression ([expr.const]) of type std​::​size_t whose value V, termed the index, is such that .
[Note 1: 
A pack-index-expression denotes the element of the pack.
— end note]

7.5.5.5 Destruction [expr.prim.id.dtor]

An id-expression that denotes the destructor of a type T names the destructor of T if T is a class type ([class.dtor]), otherwise the id-expression is said to name a pseudo-destructor.
If the id-expression names a pseudo-destructor, T shall be a scalar type and the id-expression shall appear as the right operand of a class member access ([expr.ref]) that forms the postfix-expression of a function call ([expr.call]).
[Note 1: 
Such a call ends the lifetime of the object ([expr.call], [basic.life]).
— end note]
[Example 1: 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]