注入的 class 名称可以用作友元声明中的类型名称吗?
Can an injected class name be used as a type name in a friend declaration?
考虑这段代码:
template <typename T>
class Singleton
{
};
class Logger : public Singleton<Logger> {
friend class Singleton;
};
它在 gcc 和 clang 中编译,但它有效吗? [temp.local].1 说:
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.
粗体部分似乎适用,友元声明似乎需要类型名称而不是模板名称(参见 [class.friend])。
是编译器错了还是我误读了标准?
[temp.local] that use inheritance are using a templated Derived class, thus there is a need to access the Base with a qualified name, i.e. through the Derived, as in [temp.local]#example-2中的所有示例:
template <class T> struct Base {
Base* p;
};
template <class T> struct Derived: public Base<T> {
typename Derived::Base* p; // meaning Derived::Base<T>
};
这是为了克服dependent name lookup rules.
规范的这一部分中没有 非模板派生 的示例,但如果派生不是模板化的,则以下内容也应该有效:
// same Base as above
struct Derived: public Base<int> {
Base* p; // meaning Derived::Base<int>
};
这被解释为:
struct Derived: public Base<int> {
Derived::Base* p;
};
解释为:
struct Derived: public Base<int> {
Derived::Base<int>* p;
};
在我们的案例中:
class Logger : public Singleton<Logger> {
friend class Singleton;
};
等同于:
class Logger : public Singleton<Logger> {
friend class Logger::Singleton;
};
与以下相同:
class Logger : public Singleton<Logger> {
friend class Logger::Singleton<Logger>;
};
需要注意的是injected-class-name的spec中的定义是指:
The class-name is also bound in the scope of the class (template) itself; this is known as the injected-class-name.
我会把 template
这个词出现在括号中这一事实作为规范的提示,可能会出现 injected-class-name也在非模板化 class 中。事实上,它在规范的其他地方使用,在非模板上下文中,例如 here and here.
为了关闭它,规范在 [dcl.type.simple]#note-1:
添加了
An injected-class-name is never interpreted as a template-name in contexts where class template argument deduction would be performed ([temp.local]).
所以,我会说你的代码符合规范。
请注意,[temp.local]#example-1 指的是编译器不会将 class Y
视为 Y<int>
而是 ::Y
:
的情况
template<template<class> class T> class A { };
template<class T> class Y;
template<> class Y<int> {
Y* p; // meaning Y<int>
Y<char>* q; // meaning Y<char>
A<Y>* a; // meaning A<::Y>
class B {
template<class> friend class Y; // meaning ::Y
};
};
最后一个例子也适用于我们的例子,用于声明所有类型的单身人士成为好友:
class Logger : public Singleton<Logger> {
template<class> friend class Singleton; // refers to ::Singleton
};
然而,由于 old reappearing bug in GCC.
,上述代码无法在 GCC 中编译
要克服 GCC 错误,可以使用更详细的选项:
class Logger : public Singleton<Logger> {
template<class> friend class ::Singleton; // OK with GCC and Clang
};
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.
粗体条件不适用于该示例,因为名称出现在朋友 class 声明中,而不是朋友 class template 声明中.
粗体部分适用的类似代码:
template <typename T>
class Singleton
{
};
class Logger : public Singleton<Logger> {
template <typename> friend class Singleton;
};
朋友 class 模板声明重新声明 class 模板 Singleton
并使其成为朋友。与 class 模板的第一个声明相同的语法也是合法的(参见 the example in [temp.friend]/1.4,其中 class 模板 frd
被声明并成为朋友),但第一个声明不能是注入的 class 名称的实例。
考虑这段代码:
template <typename T>
class Singleton
{
};
class Logger : public Singleton<Logger> {
friend class Singleton;
};
它在 gcc 和 clang 中编译,但它有效吗? [temp.local].1 说:
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.
粗体部分似乎适用,友元声明似乎需要类型名称而不是模板名称(参见 [class.friend])。
是编译器错了还是我误读了标准?
[temp.local] that use inheritance are using a templated Derived class, thus there is a need to access the Base with a qualified name, i.e. through the Derived, as in [temp.local]#example-2中的所有示例:
template <class T> struct Base {
Base* p;
};
template <class T> struct Derived: public Base<T> {
typename Derived::Base* p; // meaning Derived::Base<T>
};
这是为了克服dependent name lookup rules.
规范的这一部分中没有 非模板派生 的示例,但如果派生不是模板化的,则以下内容也应该有效:
// same Base as above
struct Derived: public Base<int> {
Base* p; // meaning Derived::Base<int>
};
这被解释为:
struct Derived: public Base<int> {
Derived::Base* p;
};
解释为:
struct Derived: public Base<int> {
Derived::Base<int>* p;
};
在我们的案例中:
class Logger : public Singleton<Logger> {
friend class Singleton;
};
等同于:
class Logger : public Singleton<Logger> {
friend class Logger::Singleton;
};
与以下相同:
class Logger : public Singleton<Logger> {
friend class Logger::Singleton<Logger>;
};
需要注意的是injected-class-name的spec中的定义是指:
The class-name is also bound in the scope of the class (template) itself; this is known as the injected-class-name.
我会把 template
这个词出现在括号中这一事实作为规范的提示,可能会出现 injected-class-name也在非模板化 class 中。事实上,它在规范的其他地方使用,在非模板上下文中,例如 here and here.
为了关闭它,规范在 [dcl.type.simple]#note-1:
添加了An injected-class-name is never interpreted as a template-name in contexts where class template argument deduction would be performed ([temp.local]).
所以,我会说你的代码符合规范。
请注意,[temp.local]#example-1 指的是编译器不会将 class Y
视为 Y<int>
而是 ::Y
:
template<template<class> class T> class A { };
template<class T> class Y;
template<> class Y<int> {
Y* p; // meaning Y<int>
Y<char>* q; // meaning Y<char>
A<Y>* a; // meaning A<::Y>
class B {
template<class> friend class Y; // meaning ::Y
};
};
最后一个例子也适用于我们的例子,用于声明所有类型的单身人士成为好友:
class Logger : public Singleton<Logger> {
template<class> friend class Singleton; // refers to ::Singleton
};
然而,由于 old reappearing bug in GCC.
,上述代码无法在 GCC 中编译要克服 GCC 错误,可以使用更详细的选项:
class Logger : public Singleton<Logger> {
template<class> friend class ::Singleton; // OK with GCC and Clang
};
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.
粗体条件不适用于该示例,因为名称出现在朋友 class 声明中,而不是朋友 class template 声明中.
粗体部分适用的类似代码:
template <typename T>
class Singleton
{
};
class Logger : public Singleton<Logger> {
template <typename> friend class Singleton;
};
朋友 class 模板声明重新声明 class 模板 Singleton
并使其成为朋友。与 class 模板的第一个声明相同的语法也是合法的(参见 the example in [temp.friend]/1.4,其中 class 模板 frd
被声明并成为朋友),但第一个声明不能是注入的 class 名称的实例。