c++language-lawyerclass-templatefriend-classinjected-class-name

Can an injected class name be used as a type name in a friend declaration?


Consider this code:

template <typename T>
class Singleton
{
};

class Logger : public Singleton<Logger> {
    friend class Singleton;
};

It compiles in gcc and clang, but is it valid? [temp.local].1 says:

When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type-specifier of a friend class template declaration, it is a template-name that refers to the class template itself.

The bold part seems to apply, and a friend declaration seems to require a type name and not a template name (see [class.friend]).

Are the compilers wrong or am I misreading the standard?


Solution

  • When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type-specifier of a friend class template declaration, it is a template-name that refers to the class template itself.

    The condition in bold does not apply to the example because the name appears within a friend class declaration, but not a friend class template declaration.

    Similar code where the part in bold does apply:

    template <typename T>
    class Singleton
    {
    };
    
    class Logger : public Singleton<Logger> {
        template <typename> friend class Singleton;
    };
    

    The friend class template declaration redeclares the class template Singleton and makes it a friend. The same syntax is also legal as a first declaration of a class template (see the example in [temp.friend]/1.4 where class template frd is declared and befriended), but a first declaration can't be an instance of an injected class name.