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

2024-09-25


502. Dependency of nested enumerations and enumerators

Section: 13.8.3.2  [temp.dep.type]     Status: C++11     Submitter: Mark Mitchell     Date: 05 Feb 2005

[Voted into WP at August, 2010 meeting.]

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:

    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.

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.

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.

Notes from the July, 2009 meeting:

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

Proposed resolution (June, 2010):

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

A type is dependent if it is