Search code examples
c++templatesc++17dependent-name

Nested classes are dependent types in class templates?


Consider the following:

template<class> struct T
{
    struct T1
    {
        struct T2
        {

        };
    };
    /*typename*/ T1::T2 m;
};

Without typename, compilation fails as T2 is considered a dependent name, and thus not a type. After looking to a C++17 draft standard (N4659), I believe I've determined why:

§ 17.6.2.1 ¶ 5

A name is a member of the current instantiation if it is
— An unqualified name that, when looked up, refers to at least one member of a class that is the current instantiation or a non-dependent base class thereof.
...
A name is a dependent member of the current instantiation if it is a member of the current instantiation that, when looked up, refers to at least one member of a class that is the current instantiation.

T1 is a dependent member of the current instantiation. T2 is not a member of the current instantiation (it is a member of T1),

§ 17.6.2.1 ¶ 9.3

A type is dependent if it is
...
— a nested class or enumeration that is a dependent member of the current instantiation,
...

T1 is a nested class and thus a dependent type.

§ 17.6 ¶ 3

When a qualified-id is intended to refer to a type that is not a member of the current instantiation (17.6.2.1) and its nested-name-specifier refers to a dependent type, it shall be prefixed by the keyword typename, forming a typename-specifier. ...

Thus, typename is needed.

Is my understanding correct? If so, what's the rationale behind this? How could the lookup of T1::T2 ever find anything other than the T2 nested within T1?


Solution

  • Yes, you are correct.

    In your case it doesn't matter because you cannot possibly specialize T1 without instantiating m (as it is a class member). But you could do this, put m in a function:

    template<class> struct T
    {
        struct T1
        {
            struct T2
            {
    
            };
        };
    
        void foo() {
            typename T1::T2 m;
        }
    };
    
    template<>
    struct T<void>::T1 {
        int T2;
    };
    

    If T1 were not dependent, your code could possibly change meaning because T2 would refer to a value and not a type.