[expr.prim.id]
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]
7.5.5.2 Unqualified names [expr.prim.id.unqual]
7.5.5.3 Qualified names [expr.prim.id.qual]
7.5.5.4 Pack indexing expression [expr.prim.pack.index]
7.5.5.5 Destruction [expr.prim.id.dtor]
7.5.5.1 General [expr.prim.id.general]
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
- E is potentially evaluated or C is X or a base class of X, and
- E is not the id-expression of a class member access expression ([expr.ref]), and
- E is not the id-expression of a reflect-expression ([expr.reflect]), and
- if E is a qualified-id, E is not the un-parenthesized operand of the unary & operator ([expr.unary.op]),
the id-expression is transformed into a class member access expression using (*this) as the object expression.
If this transformation occurs in the predicate of a precondition assertion of a constructor of X or a postcondition assertion of a destructor of X, the expression is ill-formed.
[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]).
[Example 1: struct C { bool b; C() pre(b) pre(&this->b) pre(sizeof(b) > 0); }; — end example]
If an id-expression E denotes a member M of an anonymous union ([class.union.anon]) U:
Otherwise, E is interpreted as a class member access ([expr.ref]) that designates the member subobject M of the anonymous union variable for U.
An id-expression or splice-expression that designates 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 or splice-expression
designates a non-static data member
and it appears in an unevaluated operand.
[Example 4: struct S { int m; }; int i = sizeof(S::m); int j = sizeof(S::m + 42); int S::*k = &[:^^S::m:]; — end example]
A splice-expression that designates a direct base class relationship shall appear only as the second operand of a class member access.
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 5: template<typename T> struct A { static void f(int) requires false; }; void g() { A<int>::f(0); void (*p1)(int) = A<int>::f; decltype(A<int>::f)* p2 = nullptr; }
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]
The terminal name of a construct is the component name of that construct that appears lexically last.
[Note 3:
If E is not declared mutable, the type of such an identifier will typically be const qualified.
— end note]
[Example 1: void f() { float x, &r = x; [=]() -> decltype((x)) { decltype(x) y1; decltype((x)) y2 = y1; decltype(r) r1 = y1; decltype((r)) r2 = y2; return y2; }; [=](decltype((x)) y) { decltype((x)) z = x; }; [=] { [](decltype((x)) y) {}; [x=1](decltype((x)) y) { decltype((x)) z = x; }; }; } — end example]
Otherwise, if the unqualified-id names a coroutine parameter, the type of the expression is that of the copy of the parameter ([dcl.fct.def.coroutine]), and the result is that copy.
Otherwise, if the unqualified-id names a result binding ([dcl.contract.res]) attached to a function with return type U,
- if U is “reference to T”, then the type of the expression is const T;
- otherwise, the type of the expression is const U.
Otherwise, if the unqualified-id appears in the predicate of a contract assertion C ([basic.contract]) and the entity is
- a variable declared outside of C of object type T,
- a variable or template parameter declared outside of C of type “reference to T”, or
- a structured binding of type T whose corresponding variable is declared outside of C,
then the type of the expression is const T.
[Example 2: int n = 0; struct X { bool m(); }; struct Y { int z = 0; void f(int i, int* p, int& r, X x, X* px) pre (++n) pre (++i) pre (++(*p)) pre (++r) pre (x.m()) pre (px->m()) pre ([=,&i,*this] mutable { ++n; ++i; ++p; ++r; ++this->z; ++z; int j = 17; [&]{ int k = 34; ++i; ++j; ++k; }(); return true; }()); template <int N, int& R, int* P> void g() pre(++N) pre(++R) pre(++(*P)); int h() post(r : ++r) post(r: [=] mutable { ++r; return true; }()); int& k() post(r : ++r); }; — end example]
Otherwise, 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 ([dcl.struct.bind]), result binding ([dcl.contract.res]), data member, or template parameter object; and a prvalue otherwise ([basic.lval]); it is a bit-field if the identifier designates a bit-field.
If an id-expression E appears in the predicate of a function contract assertion attached to a function f and denotes a function parameter of f and the implementation introduces any temporary objects to hold the value of that parameter as specified in [class.temporary],
- if the contract assertion is a precondition assertion and the evaluation of the precondition assertion is sequenced before the initialization of the parameter object, E refers to the most recently initialized such temporary object, and
- if the contract assertion is a postcondition assertion, it is unspecified whether E refers to one of the temporary objects or the parameter object; the choice is consistent within a single evaluation of a postcondition assertion.
If an id-expression E names a result binding in a postcondition assertion and the implementation introduces any temporary objects to hold the result object as specified in [class.temporary], and the postcondition assertion is sequenced before the initialization of the result object ([expr.call]), E refers to the most recently initialized such temporary object.
An implicitly movable entity is a variable with automatic storage duration that is either a non-volatile object or an rvalue reference to a non-volatile object type.
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.
[Example 1: template<int V> struct TCls { static constexpr int s = V; using type = int; }; int v1 = [:^^TCls<1>:]::s; int v2 = template [:^^TCls:]<2>::s; typename [:^^TCls:]<3>::type v3 = 3; template [:^^TCls:]<3>::type v4 = 4; typename template [:^^TCls:]<3>::type v5 = 5; [:^^TCls:]<3>::type v6 = 6; — end example]
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 entity designated by a nested-name-specifier is determined as follows:
The nested-name-specifier :: designates the global namespace.
Letting S be the specialization of T corresponding to the template argument list of the splice-specialization-specifier, S shall either be a class template specialization or an alias template specialization that denotes a class or enumeration type.
The nested-name-specifier designates the underlying entity of S.
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.
Otherwise, N designates the partial specialization ([temp.spec.partial]) of T whose template argument list is equivalent to A ([temp.over.link]); the program is ill-formed if no such partial specialization exists.
If the nested-name-specifier is not declarative, the entity shall not be a template.
If Q appears in the predicate of a contract assertion C ([basic.contract]) and the entity is
- a variable declared outside of C of object type T,
- a variable declared outside of C of type “reference to T”, or
- a structured binding of type T whose corresponding variable is declared outside of C,
then the type of the expression is const T.
Otherwise, 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]
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.
[Example 1: struct C { }; void f() { C * pc = new C; using C2 = C; pc->C::~C2(); C().C::~C(); using T = int; 0 .T::~T(); 0.T::~T(); } — end example]