the current standard makes it ill-formed to refer tostruct C { some_type m; // ... };
m
in
sizeof
expressions without providing an object. For
example, uses such as sizeof(C::m)
(outside of
C
) or sizeof(m)
(in a static member of
C
) are ill-formed.
That issue was discussed while considering core issue 198, but it was resolved to clarify the wording, but continue to not allow uses such as those outlined above.
New interest was raised with Herb Sutter's message c++std-core-11555, which showed that some support exists to make such uses well-formed.
This paper presents specific wording changes to the Working Paper, to which William Michael Miller and Clark Nelson contributed valuable comments.
there is no way to applystruct C { some_type m; // ... };
sizeof
to m
without
having an object available for use in a class member access
expression that the sizeof
operator could be apply to.
In the definition of a static member of C
, a seemingly
simple (but invalid) expression such as sizeof(m)
must be
much adorned to obtain the valid expression sizeof(((C*)
0)->m)
. A similar transformation is required for
sizeof(C::m)
.
This is counterintuitive and hard to teach.
typeof
applied to an entity other than an object of
polymorphic class type has exactly the same issues, and any future
alignof
(or similar) will raise the very same issue.
First, introduce the term "unevaluated operand" that applies to operands of specific operators where the operation is evaluated at compile-time.
Second, use that term both in the definition of the ODR and in the list of permissible uses of names of non-static members in section 5.1p10.
Finally, remove any redundant restrictions on the same subject throughout clause 9.
Change the beginning of section 3.2 basic.def.odr paragraph 2 to use the term "unevaluated operand".
Change section 4.1 conv.lval paragraph 2 as indicated:An expression is unevaluated if it is the operand of theAn expression is potentially evaluated unless it is an unevaluated operand (clause 5 expr) or a subexpression thereof.sizeof
operator (5.3.3 expr.sizeof), or if it is the operand of thetypeid
operator and it is not an lvalue of a polymorphic class type (5.2.8 expr.typeid). All other expressions are potentially evaluated.
When an lvalue-to-rvalue conversion occursAdd a new paragraph to section 5 after paragraph 8:within the operand of sizeof (5.3.3)in an unevaluated operand or a subexpression thereof (clause 5 expr) the value contained in the referenced object is not accessed, since that operator does not evaluate its operand. Otherwise, ...
Clause 5 expr specifies for some operators that some of their operands are unevaluated operands (5.3.3 expr.sizeof, 5.2.8 expr.typeid). An unevaluated operand is not evaluated. [ Note: In an unevaluated operand, a non-static class member may be named (5.1 expr.prim) and naming of objects or functions does not, by itself, require that a definition be provided (3.2 basic.def.odr). ]In section 5.1 expr.prim paragraph 10, add a bullet:
An id-expression that denotes a non-static data member or non-static member function of a class can only be used:Modify section 5.2.8 expr.typeid at the end of paragraph 3 as indicated:
[ Example:
- ..., or
- if that id-expression denotes a non-static data member and it is the sole constituent of an unevaluated operand, except for optional enclosing parentheses.
struct S { int m; }; int i = sizeof(S::m); // ok int j = sizeof(S::m + 42); // error: reference to non-static member in subexpression]
Change section 5.3.3 expr.sizeof paragraph 1 as indicated:The expression is not evaluated.The expression is an unevaluated operand (clause 5 expr).
TheReplace section 9.2 class.mem paragraph 9sizeof
operator yields the number of bytes in the object representation of its operand. The operand is either an expression, which isnot evaluatedan unevaluated operand (clause 5 expr), or a parenthesized type-id. ...
by a noteEach occurrence in an expression of the name of a non-static data member or non-static member function of a class shall be expressed as a class member access (5.2.5), except when it appears in the formation of a pointer to member (5.3.1), when it appears in the body of a non-static member function of its class or of a class derived from its class (9.3.1), or when it appears in a mem-initializer for a constructor for its class or for a class derived from its class (12.6.2).
[ Note: See 5.1 expr.prim for restrictions on the use of non-static data members and non-static member functions. ]Replace the end of section 9.4 class.static paragraph 4
...by a noteThe definition of a static member shall not use directly the names of the non-static members of its class or of a base class of its class (including as operands of the sizeof operator). The definition of a static member may only refer to these members to form pointer to members (5.3.1) or with the class member access syntax (5.2.5).
... [ Note: See 5.1 expr.prim for restrictions on the use of non-static data members and non-static member functions. ]Replace the note in section 9.7 class.nest paragraph 1
[ Note:byIn accordance with 9.2 class.mem, except by using explicit pointers, references, and object names, declarations in a nested class shall not use non-static data members or non-static member functions from the enclosing class. This restriction applies in all constructs including the operands of the sizeof operator. -- end note ]
[ Note: See 5.1 expr.prim for restrictions on the use of non-static data members and non-static member functions. -- end note ]Change one line of the example in section 9.7 class.nest paragraph 1 from
toint a = sizeof(x); // error: direct use of enclose::x even in sizeof
int a = sizeof(x); // OK: operand of sizeof is an unevaluated operand