7 Expressions [expr]

7.5 Primary expressions [expr.prim]

7.5.4 Names [expr.prim.id]

7.5.4.1 General [expr.prim.id.general]

An id-expression is a restricted form of a primary-expression.
[Note 1: — end note]
An id-expression that denotes a non-static data member or non-static member function of a class can only be used:
  • as part of a class member access in which the object expression refers to the member's class61 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 1: struct S { int m; }; int i = sizeof(S::m); // OK int j = sizeof(S::m + 42); // OK — end example]
A potentially-evaluated id-expression that denotes an immediate function shall appear only
For an id-expression that denotes an overload set, overload resolution is performed to select a unique function ([over.match], [over.over]).
[Note 2:
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 2: 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 are required to be satisfied even though f is an unevaluated operand.
— end example]
— end note]
This also applies when the object expression is an implicit (*this) ([class.mfct.non-static]).
 

7.5.4.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 decltype-specifier prefixed by ~ denotes the destructor of the type so named; see [expr.prim.id.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 result is the entity denoted by the identifier.
If the entity is a local entity and naming it from outside of an unevaluated operand within the declarative region where the unqualified-id appears would result in some intervening lambda-expression capturing it by copy ([expr.prim.lambda.capture]), 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 closure object of the innermost such intervening lambda-expression.
[Note 2:
If that lambda-expression is not declared mutable, the type of such an identifier will typically be const qualified.
— end note]
Otherwise, the type of the expression is the type of the result.
[Note 3:
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.
— end note]
[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 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) 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]

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

The type denoted by a decltype-specifier in a nested-name-specifier shall be a class or enumeration type.
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, 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 1:
A class member can be referred to using a qualified-id at any point in its potential scope ([basic.scope.class]).
— end note]
Where type-name ​::​~ type-name is used, the two type-names shall refer to the same type (ignoring cv-qualifications); this notation denotes the destructor of the type so named ([expr.prim.id.dtor]).
The unqualified-id in a qualified-id shall not be of the form ~decltype-specifier.
The nested-name-specifier ​::​ names the global namespace.
A nested-name-specifier that names a namespace ([basic.namespace]), optionally followed by the keyword template ([temp.names]), and then 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, a variable, or a structured binding ([dcl.struct.bind]) and a prvalue otherwise.
A nested-name-specifier that denotes an enumeration, 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 is first looked up in the class denoted by the nested-name-specifier of the qualified-id and the name, if found, is used.
Otherwise, it is looked up in the context in which the entire qualified-id occurs.
In each of these lookups, only names that denote types or templates whose specializations are types are considered.

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