使用模板模板形参时,模板实参推导失败
Failed template argument deduction when using template template parameters
我想创建一个简单的辅助算法,它将填充一个容器,例如std::vector<T>
,具有几何级数(第一项是a
, n
-th项由a * pow(r, n-1)
给出,其中r
是给定的比率);我创建了以下代码:
#include<vector>
#include<algorithm>
#include<iostream>
template<template <typename> class Container, typename T>
void progression(Container<T>& container, T a, T ratio, size_t N) {
if(N > 0) {
T factor = T(1);
for(size_t k=0; k<N; k++) {
container.push_back(a * factor);
factor *= ratio;
}
}
}
int main() {
std::vector<double> r;
progression(r, 10.0, 0.8, static_cast<size_t>(10));
for(auto item : r) {
std::cout<<item<<std::endl;
}
return 0;
}
在尝试编译时会产生以下错误:
$ g++ geometric.cpp -std=c++11 # GCC 4.7.2 on OS X 10.7.4
geometric.cpp: In function ‘int main()’:
geometric.cpp:18:52: error: no matching function for call to ‘progression(std::vector<double>&, double, double, size_t)’
geometric.cpp:18:52: note: candidate is:
geometric.cpp:6:6: note: template<template<class> class Container, class T> void progression(Container<T>&, T, T, size_t)
geometric.cpp:6:6: note: template argument deduction/substitution failed:
geometric.cpp:18:52: error: wrong number of template arguments (2, should be 1)
geometric.cpp:5:36: error: provided for ‘template<class> class Container’
Clang的错误信息更微妙:
$ clang++ geometric.cpp -std=c++11 # clang 3.2 on OS X 10.7.4
geometric.cpp:18:3: error: no matching function for call to 'progression'
progression(r, 10, 0.8, 10);
^~~~~~~~~~~
geometric.cpp:6:6: note: candidate template ignored: failed template argument deduction
void progression(Container<T>& container, T a, T ratio, size_t N) {
^
1 error generated.
我本来期望使用模板模板参数不仅可以推断出容器,还可以推断出容器的value_type
(在本例中为T
)。
所以,问题是:我如何创建一个泛型函数,它将能够推断容器类型和值类型?
我肯定我错过了一些明显的东西-我感谢你的耐心和帮助。
<标题>编辑(回答)以下代码的行为与预期一致:
#include<vector>
#include<algorithm>
#include<iostream>
template<template <typename...> class Container, typename T, typename... Args>
void progression(Container<Args...>& container, T a, T ratio, size_t N) {
if(N > 0) {
T factor = T(1);
for(size_t k=0; k<N; k++) {
container.push_back(a * factor);
factor *= ratio;
}
}
}
int main() {
std::vector<double> r;
progression(r, 10.0, 0.8, 10);
for(auto item : r) {
std::cout<<item<<std::endl;
}
return 0;
}
输出:10
8
6.4
5.12
4.096
3.2768
2.62144
2.09715
1.67772
1.34218
标题>std::vector<>
是一个接受两个模板参数(元素类型和分配器)的类模板,而不是一个。当您使用模板模板参数时,第二个模板参数具有默认值的事实是无关紧要的:
template<template <typename, typename> class Container, typename T, typename A>
// ^^^^^^^^ ^^^^^^^^^^
void progression(Container<T, A>& container, T a, T ratio, size_t N) {
// ^^^^
// ...
}
注意,这将导致无法传递std::map
或std::unordered_map
的实例作为第一个函数参数。因此,我的建议是放弃推断第一个参数是标准容器的实例(标准容器只是不那么统一):
template<typename C, typename T, typename A>
// ^^^^^^^^^
void progression(C& container, T a, T ratio, size_t N) {
// ^^
// ...
}
您可能想要做的是表达编译时约束,可能通过static_assert
并基于自定义类型特征,C
必须是标准容器的实例。
或者,您可以像KerrekSB在他的回答中建议的那样使用可变模板(但这仍然不会阻止您传入任何其他类型模板的实例,甚至是非容器模板)。
第二个问题是你调用模板的方式:
progression(r, 10, 0.8, 10);
第二个实参的类型是int
,而容器元素的类型是double
。这将使编译器在执行类型演绎时感到困惑。或者这样调用它:
progression(r, 10.0, 0.8, 10);
或者允许编译器为第二个参数推断出不同的类型(可能是sfinae约束它为可以转换为元素类型的东西)。
容器通常有很多模板参数。幸运的是,可变模板中有一个特殊的子句,允许使用use一个包来指定任意数量的参数:
template <template <typename...> class Container, typename ...Args>
void foo(Container<Args...> const & c)
{
// ...
}
(特殊子句的效果是,即使Container
是一个非可变变量模板,它也可以工作)
失败的原因是因为您说中间两个参数是相同的类型,而实际上它们不是。例如,中间是浮点数,而另一个是整型。基本上你说a和ratio是相同的类型但是在调用中它们是不同的类型
- 非类型引用形参/实参
- 成员函数指针的模板实参演绎
- 函数和函数作为模板函数的实参
- Const到非Const指针模板实参的转换
- 传递boost::函数,该函数接受一个模板实参作为默认为NULL的形参
- 当实参是初始化列表而形参是引用时,重载解析
- 函数模板中返回类型的模板实参演绎
- ostream_iterator的模板实参-每个元素都是pair
- c++——关于使用默认实参的困惑
- std::shared_ptr、继承和模板实参演绎的问题
- 将操作符转换为模板实参的特化
- 使用模板模板形参时,模板实参推导失败
- 带有const实参的c++构造函数
- 函数指针作为模板实参而不是函子
- c++临时对象绑定到实参并返回const引用值
- 调用对象作为默认实参的this
- 当涉及一个重载函数作为实参时,模板实参推导是如何工作的
- c++对const引用的实参依赖没有改变
- 为什么模板非类型形参指针和引用实参需要是全局的
- 将函数形参的实参解包到c++模板类