注入的类名可以用作友元声明中的类型名吗?

Nik*_*lai 5 c++ friend-class language-lawyer class-template injected-class-name

考虑这个代码:

template <typename T>
class Singleton
{
};

class Logger : public Singleton<Logger> {
    friend class Singleton;
};
Run Code Online (Sandbox Code Playgroud)

它在 gcc 和 clang 中编译,但它有效吗?[temp.local].1 说:

当它与模板参数列表一起使用时,作为模板模板参数的模板参数,或作为朋友类模板声明的详细类型说明符中的最终标识符,它是一个模板名称指的是类模板本身。

粗体部分似乎适用,并且朋友声明似乎需要类型名称而不是模板名称(参见 [class.friend])。

编译器错了还是我误读了标准?

asc*_*ler 1

当它与模板参数列表一起使用时,作为模板模板参数的模板参数,或者作为友元类模板声明的详细类型说明符中的最终标识符,它是一个模板名称指的是类模板本身。

粗体条件不适用于该示例,因为该名称出现在友元类声明中,而不是友元类模板声明中。

粗体部分适用的类似代码:

template <typename T>
class Singleton
{
};

class Logger : public Singleton<Logger> {
    template <typename> friend class Singleton;
};
Run Code Online (Sandbox Code Playgroud)

友元类模板声明重新声明类模板Singleton并使其成为友元。相同的语法作为类模板的第一个声明也是合法的(请参阅[temp.friend]/1.4 中的示例,其中类模板frd被声明并成为友元),但第一个声明不能是注入类名的实例。