This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 113d. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-03-20


198. Definition of "use" in local and nested classes

Section: 11.6  [class.local]     Status: CD1     Submitter: Erwin Unruh     Date: 27 Jan 2000

[Voted into WP at April 2003 meeting.]

11.6 [class.local] paragraph 1 says,

Declarations in a local class can use only type names, static variables, extern variables and functions, and enumerators from the enclosing scope.
The definition of when an object or function is "used" is found in 6.3 [basic.def.odr] paragraph 2 and essentially says that the operands of sizeof and non-polymorphic typeid operators are not used. (The resolution for issue 48 will add contexts in which integral constant expressions are required to the list of non-uses.)

This definition of "use" would presumably allow code like

    void foo() {
        int i;
        struct S {
            int a[sizeof(i)];
        };
    };
which is required for C compatibility.

However, the restrictions on nested classes in 11.4.12 [class.nest] paragraph 1 are very similar to those for local classes, and the example there explicitly states that a reference in a sizeof expression is a forbidden use (abbreviated for exposition):

    class enclose {
    public:
        int x;
        class inner {
            void f(int i)
            {
                int a = sizeof(x);  // error: refers to enclose::x
            }
        };
    };

[As a personal note, I have seen real-world code that was exactly like this; it was hard to persuade the author that the required writearound, sizeof(((enclose*) 0)->x), was an improvement over sizeof(x). —wmm]

Similarly, 11.4 [class.mem] paragraph 9 would appear to prohibit examples like the following:

    struct B {
        char x[10];
    };
    struct D: B {
        char y[sizeof(x)];
    };

Suggested resolution: Add cross-references to 6.3 [basic.def.odr] following the word "use" in both 11.4.12 [class.nest] and 11.6 [class.local] , and change the example in 11.4.12 [class.nest] to indicate that a reference in a sizeof expression is permitted. In 11.4 [class.mem] paragraph 9, "referred to" should be changed to "used" with a cross_reference to 6.3 [basic.def.odr].

Notes from 10/01 meeting:

It was noted that the suggested resolution did not make the sizeof() example in 11.4.12 [class.nest] valid. Although the reference to the argument of sizeof() is not regarded as a use, the right syntax must be used nonetheless to reference a non-static member from the enclosing class. The use of the member name by itself is not valid. The consensus within the core working group was that nothing should be done about this case. It was later discovered that 11.4.9 [class.static] paragraph 3 states that

The definition of a static member shall not use directly the names of the nonstatic 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 (7.6.2.2 [expr.unary.op]) or with the class member access syntax (7.6.1.5 [expr.ref]).

This seems to reinforce the decision of the working group.

The use of "use" should still be cross-referenced. The statements in 11.4.12 [class.nest] and 11.6 [class.local] should also be rewritten to state the requirement positively rather than negatively as the list of "can't"s is already missing some cases such as template parameters.

Notes from the 4/02 meeting:

We backed away from "use" in the technical sense, because the requirements on the form of reference are the same whether or not the reference occurs inside a sizeof.

Proposed Resolution (revised October 2002):

In 11.4 [class.mem] paragraph 9, replace

Except when used to form a pointer to member (7.6.2.2 [expr.unary.op]), when used in the body of a nonstatic member function of its class or of a class derived from its class (11.4.3 [class.mfct.non.static]), or when used in a mem-initializer for a constructor for its class or for a class derived from its class (11.9.3 [class.base.init]), a nonstatic data or function member of a class shall only be referred to with the class member access syntax (7.6.1.5 [expr.ref]).

with the following paragraph

Each occurrence in an expression of the name of a nonstatic data member or nonstatic member function of a class shall be expressed as a class member access (7.6.1.5 [expr.ref]), except when it appears in the formation of a pointer to member (7.6.2.2 [expr.unary.op]), when it appears in the body of a nonstatic member function of its class or of a class derived from its class (11.4.3 [class.mfct.non.static]), or when it appears in a mem-initializer for a constructor for its class or for a class derived from its class (11.9.3 [class.base.init]).

In 11.4.12 [class.nest] paragraph 1, replace the last sentence,

Except by using explicit pointers, references, and object names, declarations in a nested class can use only type names, static members, and enumerators from the enclosing class.

with the following

[Note: In accordance with 11.4 [class.mem], except by using explicit pointers, references, and object names, declarations in a nested class shall not use nonstatic data members or nonstatic member functions from the enclosing class. This restriction applies in all constructs including the operands of the sizeof operator.]

In the example following 11.4.12 [class.nest] paragraph 1, change the comment on the first statement of function f to emphasize that sizeof(x) is an error. The example reads in full:

  int x;
  int y;
  class enclose {
  public:
    int x;
    static int s;
    class inner {
      void f(int i)
      {
        int a = sizeof(x);  // error: direct use of enclose::x even in sizeof
        x = i;              // error: assign to enclose::x
        s = i;              // OK: assign to enclose::s
        ::x = i;            // OK: assign to global x
        y = i;              // OK: assign to global y
      }
      void g(enclose* p, int i)
      {
        p->x = i;        // OK: assign to enclose::x
      }
    };
  };

  inner* p = 0;             // error: inner not in scope