我在我的C ++项目中使用模板,当使用模板化类型作为模板模板参数时,我遇到了问题。我认为描述它的最好方法是给出一个产生错误的例子:
template <template<class> class P, typename T>
class Foo {
P<T> baz;
};
template <class T>
class Bar {
Foo<Bar, T> memberFoo;
void makeFoo() {
Foo<Bar, T>* f = new Foo<Bar, T>();
}
};
Foo<Bar, int> globalFoo;
globalFoo
的声明不会导致错误,但memberFoo
和f
的声明会导致编译错误:
错误:模板模板参数的模板参数必须是类模板 或输入别名模板
只有在Bar类声明中使用Bar作为模板参数时才会出现错误,但同时使用clang和g ++。这看起来似乎会在某处记录,但谷歌搜索不会产生任何问题或其他文档。
这种模板的使用在C ++中是不合法的,还是我误解了如何定义和使用模板?如果C ++ 11标准不允许这种设计架构,我可以使用什么解决方法?
答案 0 :(得分:2)
问题是在实例化时会产生不完整的类型。 Clang和G ++会产生不同的错误,因为Clang(显然)没有实现C ++ 11规则,当用作模板模板参数时,注入的类名称可以引用类模板本身(Igor的建议没有& #39; t btw。)将Bar
更改为::Bar
修复了此错误,这使得Clang指出了像G ++那样的不完整错误。将baz
更改为P<T>*
可以进行编译。
N.B。即使编译,也可能是未定义的行为。我建议重新设计你的课程。
答案 1 :(得分:0)
基于@Igor的评论,我已经找到了解决这个问题的几个方法。基于引用 Bar
在其声明中引用 Bar
的特殊化(引用@Igor)这一事实。
请注意,所有这些变通办法都依赖于baz
可以声明为指针的事实。 baz
不是指针导致@Nir在注释中提到的递归问题并导致错误:
字段的类型不完整&f;&lt; Bar&lt; int&gt;,int&gt;&#39;
解决方法1
添加Bar
的转发声明并创建模板别名Bar2
。为了编译,baz
必须是一个指针:
template <template <typename> class P, typename T>
class Foo {
P<T>* baz;
};
template<typename T> class Bar;
template <typename U> using Bar2 = Bar<U>;
template <class T>
class Bar {
Foo<Bar2, T> memberFoo;
void makeFoo() {
Foo<Bar2, T>* f = new Foo<Bar2, T>();
}
};
Foo<Bar, int> globalFoo;
模板别名的前向声明和使用强制编译器在定义Bar
和memberFoo
时使用非专业版f
,而默认情况下使用非专用版本globalFoo
的定义。
解决方法2
此解决方法基于@ user5800314的答案。我觉得没有必要重新陈述他的解决方法,但我觉得值得注意它的工作原因。
我已经阅读了关于注入类名和C ++ 11的similar SO question,但这里的重要区别是我的代码不能在g ++上编译,而他们的代码却是。我不相信这个问题是缺乏注入类名的实现。我相信此解决方法可以修复编译错误,因为使用::Bar
而不是Bar
会再次强制编译器访问Bar
的全局(非专业化)版本,而不是访问本地(专用)版本Bar
。
解决方法3
将Foo
指定为具有类(或typename)模板参数而不是模板模板参数,并明确指出在使用Foo
模板时正在使用哪种特化。这还要求baz
是一个指针,并且它不使用模板类型:
template <class P, typename T>
class Foo {
P* baz;
};
template <class T>
class Bar {
Foo<Bar, T> memberFoo;
void makeFoo() {
Foo<Bar, T>* f = new Foo<Bar, T>();
}
};
Foo<Bar<int>, int> globalFoo;
此解决方法通过要求向Foo
模板提供特定类来解决模板模板参数的潜在混淆。在某些情况下,此解决方法可能无法使用,但在其他情况下可能是一种优雅的解决方案。例如,在我的情况下,我不需要从Foo
之外实例化Bar
的实例,所以这是解决编译错误的一种非常好的方法。
PS 我真的很感谢@ user5800314,因为他的解决方法工作,但是我在这里提供了不同的解释,因为我在这里提供的解释是什么我认为是正确的,我不觉得我可以将@ user5800314的答案标记为已接受。