在成员变量中使用模板类作为模板模板参数时出错
Error when using template class as template template-parameter in member variable
我在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++ 时会发生此错误。这似乎是在某处记录的东西,但谷歌搜索不会产生SO问题或其他文档。
这种模板的使用在C++中是不合法的,还是我对如何定义和使用模板产生了误解?如果 C++11 标准不允许此设计架构,我可以使用哪种解决方法?
实例化时生成不完整的类型。Clang和G ++会产生不同的错误,因为Clang(显然)没有实现C++11规则,即注入的类名在用作模板模板参数时可以引用类模板本身(顺便说一句,Igor的建议不起作用。将Bar
更改为::Bar
可以修复此错误,这使得Clang像G ++一样指出不完整的错误。将baz
更改为P<T>*
允许它进行编译。
:注:即使它编译,它也可能是未定义的行为。我建议重新设计你的课程。
根据@Igor的评论,我想出了这个问题的几种解决方法。基于这样一个事实,即在其声明中引用Bar
是指Bar
的这种专业化(引用@Igor)。
请注意,所有这些解决方法都依赖于这样一个事实,即baz
可以声明为指针。 baz
不是指针会导致@Nir在注释中提到的递归问题并导致错误:
字段具有不完整的类型"Foo
,int>"
解决方法 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;
模板别名的前向声明和使用强制编译器在定义memberFoo
和f
时使用非专用版本的Bar
,而globalFoo
的定义默认使用非专用版本。
解决方法 2
此解决方法基于 @user5800314 的答案。我觉得没有必要重申他的解决方法,但我确实觉得值得注意它起作用的原因。
我已经读过一个关于注入类名和 C++11 的类似 SO 问题,但这里的重要区别在于我的代码不在 g++ 上编译,而他们的代码在 g++ 上编译。我不认为问题在于缺乏注入类名的实现。我相信此解决方法可以修复编译错误,因为再次使用::Bar
而不是Bar
会强制编译器访问Bar
的全局(非专用)版本,而不是访问Bar
的本地(专用)版本。
解决方法 3
将Foo
指定为具有类(或类型名)模板参数而不是模板模板参数,并在使用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
模板提供特定类来解决模板模板参数的潜在混淆问题。此解决方法在某些情况下可能不可用,但在其他情况下可能是一个优雅的解决方案。例如,就我而言,我不需要从Bar
外部实例化Foo
实例,因此这是解决编译错误的非常好的方法。
附言我真的很想感谢@user5800314,因为他的解决方法确实有效,但是我在这里提供了不同的解释,并且由于我在这里提供的解释是我认为正确的,我认为我不能将@user5800314的答案标记为接受。
- 为 NewObjectA() 函数创建 jvalues 的参数数组时出错 - JNI Invocation API
- 将成员函数作为构造函数参数调用时出错 "Variable is not a type name"
- 使用一个参数的模板函数时出错(适用于 2)
- 为什么在函数参数前面添加 const 会出错?
- 变量用作C中的函数参数后出错
- 将私有数据成员作为默认参数传递给该类的公共方法时出错
- 使用 cvThreshold 时报告参数时出错
- STL 容器在函数中作为模板参数,在调用中出错
- 使用模板模板参数时出错
- 方法的参数列表出错
- 将 std::bind 应用于带有参数 <boost::asio::ip::tcp::socket> 时出错?
- 使用模板函数传递模板参数时出错
- 在 c++ 中调用将字符串作为参数传递的函数时出错
- 在运算符中将联合作为参数传递时出错<<
- 在C 中使用Caffe;无法将Caffe ::求解参数仪发挥作用而不会出错
- 尝试使用浮点参数运行重载函数时出错
- 尝试将 C++11 代码转换为 C++03 时默认函数模板参数出错
- Lua c++ lib sethook:钩子函数参数出错
- 在 Unix 中编译 c++ 时嵌套模板参数出错
- uniform_real_distribution c++的最小/最大参数出错