Doc. no.: N2253=07-0113
Date: 2007-04-19
Project: Programming Language C++
Reply-to: Jens Maurer <Jens.Maurer@gmx.net>

Extending sizeof to apply to non-static data members without an object (revision 1)

This paper is a revision of N2150 "Extending sizeof to apply to non-static data members without an object" by Jens Maurer.

1 Introduction

Given some class C with a data member m,
struct C {
   some_type m;
   // ...
};
the current standard makes it ill-formed to refer to 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.

2 Problem Description

Given some class C with a data member m,
struct C {
   some_type m;
   // ...
};
there is no way to apply 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.

3 Proposed Solution

Make the indicated uses valid by introducing the following changes.

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.

4 Proposed Changes to the Working Paper

Drafting note: The decltype proposal should define its argument to be an "unevaluated operand", that proposal should not alter 3.2 basic.def.odr.

Change the beginning of section 3.2 basic.def.odr paragraph 2 to use the term "unevaluated operand".

An expression is unevaluated if it is the operand of the sizeof operator (5.3.3 expr.sizeof), or if it is the operand of the typeid operator and it is not an lvalue of a polymorphic class type (5.2.8 expr.typeid). All other expressions are potentially evaluated. An expression is potentially evaluated unless it is an unevaluated operand (clause 5 expr) or a subexpression thereof.
Change section 4.1 conv.lval paragraph 2 as indicated:
When an lvalue-to-rvalue conversion occurs 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, ...
Add a new paragraph to section 5 after paragraph 8:
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:

[ Example:
struct S {
  int m;
};
int i = sizeof(S::m);       // ok
int j = sizeof(S::m + 42);  // error: reference to non-static member in subexpression
]
Modify section 5.2.8 expr.typeid at the end of paragraph 3 as indicated:
The expression is not evaluated. The expression is an unevaluated operand (clause 5 expr).
Change section 5.3.3 expr.sizeof paragraph 1 as indicated:
The sizeof operator yields the number of bytes in the object representation of its operand. The operand is either an expression, which is not evaluated an unevaluated operand (clause 5 expr), or a parenthesized type-id. ...
Replace section 9.2 class.mem paragraph 9
Each 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).
by a note
[ 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
... The 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).
by a note
... [ 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: In 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 ]
by
[ 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
     int a = sizeof(x);   // error: direct use of enclose::x even in sizeof
to
     int a = sizeof(x);   // OK: operand of sizeof is an unevaluated operand