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


224. Definition of dependent names

Section: 13.8.3.2  [temp.dep.type]     Status: CD1     Submitter: Derek Inglis     Date: 30 Nov 1999

[Moved to DR at 10/01 meeting.]

The definition of when a type is dependent, given in 13.8.3.2 [temp.dep.type], is essentially syntactic: if the reference is a qualified-id and one of the class-names in the nested-name-specifier is dependent, the type is dependent. This approach leads to surprising results:

    template <class T> class X {
        typedef int I;
	I a;                 // non-dependent
        typename X<T>::I b;  // dependent
        typename X::I c;     // dependent (X is equivalent to X<T>)
    };

Suggested resolution:

The decision on whether a name is dependent or non-dependent should be based on lookup, not on the form of the name: if the name can be looked up in the definition context and cannot be anything else as the result of specialization, the name should be non-dependent.

See papers J16/00-0028 = WG21 N1251 and J16/00-0056 = WG21 N1279.

Proposed resolution (10/00):

  1. Replace section 13.8.3.2 [temp.dep.type] with the following:

    In the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, a name refers to the current instantiation if it is

    • the injected-class-name (Clause 11 [class]) of the class template or nested class,
    • in the definition of a primary class template, the name of the class template followed by the template argument list of the primary template (as described below) enclosed in <>,
    • in the definition of a nested class of a class template, the name of the nested class referenced as a member of the current instantiation, or
    • in the definition of a partial specialization, the name of the class template followed by the template argument list of the partial specialization enclosed in <>.

    The template argument list of a primary template is a template argument list in which the nth template argument has the value of the nth template parameter of the class template.

    A template argument that is equivalent to a template parameter (i.e., has the same constant value or the same type as the template parameter) can be used in place of that template parameter in a reference to the current instantiation. In the case of a nontype template argument, the argument must have been given the value of the template parameter and not an expression involving the template parameter.

    [Example:

    template <class T> class A {
        A* p1;      // A is the current instantiation
        A<T>* p2;   // A<T> is the current instantiation
        A<T*> p3;   // A<T*> is not the current instantiation
        ::A<T>* p4; // ::A<T> is the current instantiation
        class B {
    	B* p1;        // B is the current instantiation
    	A<T>::B* p2;  // A<T>::B is the current instantiation
    	typename A<T*>::B* p3; // A<T*>::B is not the
    			     // current instantiation
        };
    };
    
    template <class T> class A<T*> {
        A<T*>* p1;  // A<T*> is the current instantiation
        A<T>* p2;   // A<T> is not the current instantiation
    };
    
    template <class T1, class T2, int I> struct B {
        B<T1, T2, I>*	b1;        // refers to the current instantiation
        B<T2, T1, I>*	b2;        // not the current instantiation
        typedef T1 my_T1;
        static const int my_I = I;
        static const int my_I2 = I+0;
        static const int my_I3 = my_I;
        B<my_T1, T2, my_I>* b3;  // refers to the current instantiation
        B<my_T1, T2, my_I2>* b4; // not the current instantiation
        B<my_T1, T2, my_I3>* b5; // refers to the current instantiation
    };
    

    end example]

    A name is a member of the current instantiation if it is

    • An unqualified name that, when looked up, refers to a member of a class template. [Note: This can only occur when looking up a name in a scope enclosed by the definition of a class template.]
    • A qualified-id in which the nested-name-specifier refers to the current instantiation.

    [Example:

    template <class T> class A {
        static const int i = 5;
        int n1[i];        // i refers to a member of the current instantiation
        int n2[A::i];     // A::i refers to a member of the current instantiation
        int n3[A<T>::i];  // A<T>::i refers to a member of the current instantiation
        int f();
    };
    
    template <class T> int A<T>::f()
    {
        return i;  // i refers to a member of the current instantiation
    }
    

    end example]

    A name is a member of an unknown specialization if the name is a qualified-id in which the nested-name-specifier names a dependent type that is not the current instantiation.

    A type is dependent if it is

    • a template parameter,
    • a member of an unknown specialization,
    • a nested class that is a member of the current instantiation,
    • a cv-qualified type where the cv-unqualified type is dependent,
    • a compound type constructed from any dependent type,
    • an array type constructed from any dependent type or whose size is specified by a constant expression that is value-dependent, or
    • a template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent.

    [Note: Because typedefs to not introduce new types, but instead simply refer to other types, a name that refers to a typedef that is a member of the current instantiation is dependent only if the type referred to is dependent.]

  2. In 13.8.3.3 [temp.dep.expr] paragraph 3, replace

    • a nested-name-specifier that contains a class-name that names a dependent type.

    with

    • a nested-name-specifier or qualified-id that names a member of an unknown specialization.
  3. In 13.8.3.3 [temp.dep.expr], add the following paragraph:

    A class member access expression (7.6.1.5 [expr.ref]) is type-dependent if the type of the referenced member is dependent. [Note: In an expression of the form x.y or xp->y the type of the expression is usually the type of the member y of the class of x (or the class pointed to by xp). However, if x or xp refers to a dependent type that is not the current instantiation, the type of y is always dependent. If x or xp refers to a non-dependent type or refers to the current instantiation, the type of y is the type of the class member access expression.]
  4. In 13.8 [temp.res] paragraph 3, replace

    A qualified-name that refers to a type and that depends on a template-parameter (13.8.3 [temp.dep]) shall be prefixed by the keyword typename.

    with

    A qualified-id that refers to a type and that depends on a template-parameter (13.8.3 [temp.dep]) but does not refer to a member of the current instantiation shall be prefixed by the keyword typename.

    Note: the wording for this paragraph was changed in TC1. The words shown here are the pre-TC1 words.

  5. In 13.3 [temp.names] paragraph 4, replace

    When the name of a member template specialization appears after . or -> in a postfix-expression, or after a nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (13.8.3 [temp.dep]), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.

    with

    When the name of a member template specialization appears after . or -> in a postfix-expression, or after a nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (13.8.3 [temp.dep]) but does not refer to a member of the current instantiation (13.8.3.2 [temp.dep.type]), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.
  6. In 13.8.2 [temp.local] paragraph 2, remove the following text, which was added for issue 108. The updated definition of dependent name now addresses this case.

    Within the scope of a class template, when the unqualified name of a nested class of the class template is referred to, it is equivalent to the name of the nested class qualified by the name of the enclosing class template. [Example:

    template <class T> struct A {
    	class B {};
    	// B is equivalent to A::B, which is equivalent to A<T>::B,
    	// which is dependent.
    	class C : B { };
    };
    

    end example]