具有模板模板参数的CRTP
CRTP with Template Template Arguments
以下代码没有编译。。。
namespace {
template<typename T, template<typename> class D>
struct Base {
Base(const T& _t) : t(_t) { }
T t;
};
template<typename T>
struct Derived : Base<T, Derived> {
Derived(const T& _t) : Base<T, Derived>(_t) { }
};
}
int main(int argc, char* argv[]) {
Derived<int> d(1);
return 0;
}
线路Derived(const T& _t) : Base<T, Derived>(_t) { }
上存在编译错误
错误C3200"匿名命名空间"::派生的:无效模板模板参数"D"的参数,应为类模板
如果我提供任何其他具有模板参数而不是派生自身的类,这就有效了
template<typename T>
struct Other {
};
template<typename T>
struct Derived : Base<T, Other> {
Derived(const T& _t) : Base<T, Other>(_t) { }
};
Tl;dr:解决这个问题的最便携、最不广泛的方法似乎是在您的示例中使用限定名::Derived
:
template<typename T>
struct Derived : Base<T, Derived>
{
Derived(const T& _t) : Base<T, ::Derived>(_t) { }
};
为什么
问题是编译器不符合C++11。
与普通(非模板)类一样,类模板有一个注入的类名(第9条)注入的类名可以用作模板名或类型名当它与模板参数列表一起使用时,用作模板模板参数的模板参数,或者用作友类模板声明的详细类型说明符中的最终标识符时,它指的是类模板本身
因此,您的代码应该进行编译,但不幸的是,我测试过的所有编译器(clang 3.7、Visual Studio 2015和g++5.3)都拒绝这样做
在Derived
定义中,您应该能够以各种方式表示模板:
- 直接使用注入的名称
Derived
- 类模板的合格名称
::Derived
- 使用注入的类名,将其指定为模板名
Derived<T>::template Derived
- 使用限定名称,再次将其指定为模板名称
::template Derived
关于这四个选项,这些编译器的编译状态如下(使用匿名名称空间;其中+
=成功编译):
+------------------------------+----------+---------+-----------+
| Method | MSVS2015 | g++ 5.3 | clang 3.7 |
+------------------------------+----------+---------+-----------+
| Derived | - | - | - |
| ::Derived | + | + | + |
| Derived<T>::template Derived | - | - | + |
| ::template Derived | + | - | + |
+------------------------------+----------+---------+-----------+
当将名称空间命名为X
时,图片会发生一些变化(即g++
现在接受X::template Derived
,而拒绝::template Derived
):
+---------------------------------+----------+---------+-----------+
| Method | MSVS2015 | g++ 5.3 | clang 3.7 |
+---------------------------------+----------+---------+-----------+
| Derived | - | - | - |
| X::Derived | + | + | + |
| X::Derived<T>::template Derived | - | - | + |
| X::template Derived | + | + | + |
+---------------------------------+----------+---------+-----------+
在类模板中,注入的类名(示例中为Derived
)既可以是类型名,也可以是模板名。该标准规定,当用作模板模板参数的参数时,应考虑命名模板(因此您的代码应该可以工作),但不幸的是,一些编译器尚未实现这一点。
一种解决方法是使用限定名称,这样就不用注入的类名,而是直接命名模板:
template<typename T>
struct Derived : Base<T, Derived> {
Derived(const T& _t) : Base<T, ::Derived>(_t) { }
};
相关文章:
- 如何反转整数参数包
- 为什么 CRTP 模板C++给出无效参数错误?
- 如何使用 CRTP 创建具有基类的可选模板参数
- 有没有一种干净(更)的方法将 CRTP 与可变参数继承混合在一起?
- 用static_cast(*this)...初始化可变参数CRTP:<Base>合法吗?
- 使用派生的模板参数类型作为函数 (CRTP) 的返回类型
- CRTP:具有基于派生参数的函数
- 如何将模板参数传递给CRTP
- 奇怪的重复模板模式(CRTP)和派生的构造函数参数
- Boost.Parameter:与 CRTP 组合的命名模板参数
- 如何使参数化的基础成为 CRTP 中派生的朋友
- 具有模板模板参数的CRTP
- 可以使用CRTP和以接口为参数的函数吗
- 在CRTP中使用模板参数的嵌套类
- clang++在使用CRTP时不接受使用模板模板参数
- CRTP+可变模板+提取CRTP子类参数
- 使用模板模板参数与概念中的CRTP
- 如何在使用CRTP时获取模板参数的大小
- 带有参数化基类的c++ CRTP
- 使用奇怪循环模板模式(CRTP)和其他类型参数