c++ - 注入的类名可以用作友元声明中的类型名吗?
问题描述
考虑这段代码:
template <typename T>
class Singleton
{
};
class Logger : public Singleton<Logger> {
friend class Singleton;
};
它在 gcc 和 clang 中编译,但它有效吗?[temp.local].1 说:
当它与模板参数列表一起使用时,作为模板模板参数的模板参数,或作为朋友类模板声明的详细类型说明符中的最终标识符,它是一个模板名称指类模板本身。
粗体部分似乎适用,朋友声明似乎需要类型名称而不是模板名称(参见 [class.friend])。
是编译器错误还是我误读了标准?
解决方案
[temp.local]中使用继承的所有示例都使用模板化 Derived class,因此需要使用限定名称访问 Base ,即通过 Derived,如[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>
};
这是为了克服依赖名称查找规则。
在规范的这一部分中没有非模板 Derived的示例,但如果 Derived 未模板化,则以下内容也应该有效:
// 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规范中的定义是指:
类名也绑定在类(模板)本身的范围内;这被称为注入类名。
我认为这个词template
出现在括号中,作为规范的提示,注入的类名也可能出现在非模板类中。事实上,它在规范的其他地方使用,在非模板上下文中,例如here和here。
为了解决这个问题,规范在[dcl.type.simple]#note-1处添加:
在执行类模板参数推导的上下文中([temp.local]),注入的类名永远不会被解释为模板名。
因此,我会说您的代码符合规范。
请注意,[temp.local]#example-1指的是编译器不会看到class Y
asY<int>
而是 as 的情况::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
};
};
最后一个示例也适用于我们的案例,用于声明所有类型的 Singleton 都已成为朋友:
class Logger : public Singleton<Logger> {
template<class> friend class Singleton; // refers to ::Singleton
};
但是,由于 GCC 中出现了一个旧的重新出现的错误,上述内容无法在 GCC中编译。
为了克服 GCC 错误,可以使用更详细的选项:
class Logger : public Singleton<Logger> {
template<class> friend class ::Singleton; // OK with GCC and Clang
};
推荐阅读
- r - Cannot convert vector of different formats to POSIXct
- c++11 - 调用内核时 CUDA 中的类型不匹配
- django - 使用 Django 的唯一字段来减小数据库大小
- rust - 在异步任务中读取标准输入时,“必须从 Tokio 运行时的上下文中调用阻塞带注释的 I/O”
- r - 调整使用 lmerTest::lmer() 获得的 p 值以进行多重比较
- java - 获取 java.lang.NoSuchMethodException: io.jsonwebtoken.impl.crypto.MacProvider.generateKey 仅在 jUnits 的情况下
- php - Laravel:在 logging.php 配置之外添加日志通道(插件开发)
- python - 从数据框字典中,如何获取基于特定行的最接近值的列
- oracle - 程序问题 ORA-00904 无效标识符
- javascript - 使用 NextJS 的自托管字体