Title
Dependency of nested enumerations and enumerators
Status
c++11
Section
13.8.3.2 [temp.dep.type]
Submitter
Mark Mitchell

Created on 2005-02-05.00:00:00 last changed 96 months ago

Messages

Date: 2010-08-15.00:00:00

[Voted into WP at August, 2010 meeting.]

Date: 2010-06-15.00:00:00

Proposed resolution (June, 2010):

Change 13.8.3.2 [temp.dep.type] paragraph 6 as follows:

A type is dependent if it is

  • ...

  • a nested class or enumeration that is a member of the current instantiation,

  • ...

Date: 2009-07-15.00:00:00

Notes from the July, 2009 meeting:

The consensus of the CWG was that all the types and values are dependent.

Date: 2005-10-15.00:00:00

Notes from the October, 2005 meeting:

There was no consensus among the CWG regarding question #3 (which enumerators should be considered value-dependent). The argument in favor of 3.C is principally that the values of enumerators with non-dependent initializers are known at definition time, so there is no need to treat them as dependent.

One objection to 3.C is that, according to the consensus of the CWG, the enumeration type is dependent and thus even the known values of the enumeration would have a dependent type, which could affect the results when such enumerations are used in expressions. A possible response to this concern would be to treat non-dependent initializers as having the type of the initializer rather than the enumeration type, similar to the treatment of enumerators within the enumerator-list (9.7.1 [dcl.enum] paragraph 5). However, this approach would be inconsistent with the treatment of other enumeration types. It would also interfere with overload resolution (e.g., the call in the example under question #1 above would resolve to f(int) with this approach rather than f(S<int>::E)).

Those in favor of option 3.A also suggested that it would be simpler and require less drafting: if all the enumerators have the (dependent) type of the enumeration, 13.8.3.4 [temp.dep.constexpr] paragraph 2 already says that a name with a dependent type is value-dependent, so nothing further would need to be said. Option 3.C would require additional caveats to exempt some enumerators.

The proponents of 3.A also pointed out that there are many other cases where a known value with a dependent type is treated as dependent:

    static const T t = 0;
    ... A<t> ...

or

    template <int I> void f() {
        g(I-I);
    }

With regard to current practice, g++ and MSVC++ implement 3.A, while EDG implements 3.C.

Date: 2005-04-15.00:00:00

Notes from the April, 2005 meeting:

The CWG agreed on the following positions:

  1. Nested enumerations are dependent types.

  2. The result of the sizeof operator applied to a nested enumeration is value-dependent unless there are no dependent initializers in its definition; the first case above is not dependent, while the second and third are dependent.

  3. The approach described in 3.C above is correct. This is similar to the treatment of static const integral data members, which are dependent only if their initializer is dependent.

Date: 2005-02-05.00:00:00

The Standard is currently silent on the dependency status of enumerations and enumerators that are members of class templates. There are three questions that must be answered in this regard:

  1. Are enumeration members of class templates dependent types?

    It seems clear that nested enumerations must be dependent. For example:

        void f(int);
    
        template<typename T> struct S {
            enum E { e0 };
            void g() {
                f(e0);
            }
        };
    
        void f(S<int>::E);
    
        void x() {
            S<int> si;
            si->g();       // Should call f(S<int>::E)
        }
    
  2. Is sizeof applied to a nested enumeration a value-dependent expression (13.8.3.4 [temp.dep.constexpr])?

    There are three distinct cases that might have different answers to this question:

    •     template<typename T> struct S {
              enum E { e0 };
          };
      

      Here, the size of E is, in principle, known at the time the template is defined.

    •     template<short I> struct S {
              enum E { e0 = I };
          };
      

      In this case, the minimum size required for E cannot be determined until instantiation, but it is clear that the underlying type need be no larger than short.

    •     template<typename T> struct S {
              enum E { e0 = T::e0; };
          }
      

      Here, nothing can be known about the size of E at the time the template is defined.

    13.8.3.4 [temp.dep.constexpr] paragraph 2 says that a sizeof expression is value-dependent if the type of the operand is type-dependent. Unless enumerations are given special treatment, all three of these examples will have value-dependent sizes. This could be surprising for the first case, at least, if not the second as well.

  3. Are nested enumerators value-dependent expressions?

    Again the question of dependent initializers comes into play. As an example, consider:

        template<short I> struct S {
            enum E { e0, e1 = I, e2 };
        };
    

    There seem to be three possible approaches as to whether the enumerators of E are value-dependent:

    1. The enumerators of a nested enumeration are all value-dependent, regardless of whether they have a value-dependent initializer or not. This is the current position of 13.8.3.4 [temp.dep.constexpr] paragraph 2, which says that an identifier is value-dependent if it is a name declared with a dependent type.

    2. The enumerators of a nested enumeration are all value-dependent if any of the enumeration's enumerators has a value-dependent initializer. In this approach, e0 would be value-dependent, even though it is clear that it has the value 0.

    3. An enumerator of a nested enumeration is value-dependent only if it has a value-dependent initializer (explict or implicit). This approach would make e1 and e2 value-dependent, but not e0.

    An example that bears on the third approach is the following:

        template<typename T> struct S {
            enum E { N = UINT_MAX, O = T::O };
            int a[N + 2];
        };
    

    With the normal treatment of enumerations, the type of a might be either int[UINT_MAX+2] or int[1], depending on whether the value of T::O was such that the underlying type of E is unsigned int or long.

    One possibility for addressing this problem under the third approach would be to treat a given enumerator as having the type of its initializer in such cases, rather than the enumeration type. This would be similar to the way enumerators are treated within the enumerator list, before the enumeration declaration is complete (9.7.1 [dcl.enum] paragraph 5). The argument against this is that it makes arithmetic using enumerators behave differently when the enumeration is a member of a class template and when it is not.

History
Date User Action Args
2014-03-03 00:00:00adminsetstatus: fdis -> c++11
2011-04-10 00:00:00adminsetstatus: wp -> fdis
2010-11-29 00:00:00adminsetstatus: dr -> wp
2010-08-23 00:00:00adminsetmessages: + msg2927
2010-08-23 00:00:00adminsetstatus: review -> dr
2010-02-16 00:00:00adminsetmessages: + msg2560
2010-02-16 00:00:00adminsetstatus: drafting -> review
2009-08-03 00:00:00adminsetmessages: + msg2204
2009-08-03 00:00:00adminsetstatus: open -> drafting
2005-10-22 00:00:00adminsetmessages: + msg1268
2005-05-01 00:00:00adminsetmessages: + msg1185
2005-02-05 00:00:00admincreate