[英]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''anonymous-namespace':: Derived':模板参数'D'的模板参数无效,需要一个类模板
如果我提供任何其他具有模板参数而不是Derived本身的类,则此方法有效
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节)。 注入的类名可以用作模板名或类型名。 当它与template-argument-list一起使用时, 作为模板模板参数的模板参数 ,或作为友元类模板声明的详细说明类型说明符中的最终标识符, 它引用类模板本身。
因此,您的代码应该编译,但遗憾的是,我测试的所有编译器(clang 3.7,Visual Studio 2015和g ++ 5.3)都拒绝这样做。
Afaik,你应该能够在Derived
定义中以各种方式表示模板:
Derived
::Derived
Derived<T>::template Derived
::template Derived
这些编译器关于这四个选项的编译状态如下(使用anonymus命名空间;其中+
=成功编译):
+------------------------------+----------+---------+-----------+
| 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 | + | + | + |
+---------------------------------+----------+---------+-----------+
在类模板中,inject-class-name(在您的示例中为Derived
)可以是类型名称和模板名称。 该标准指定在用作模板模板参数的参数时应该考虑将模板命名(因此您的代码应该起作用),但遗憾的是一些编译器尚未实现此功能。
一种解决方法是使用限定名称,这样您就不会使用inject-class-name,而是直接命名模板:
template<typename T>
struct Derived : Base<T, Derived> {
Derived(const T& _t) : Base<T, ::Derived>(_t) { }
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.