Esc*_*alo 0 c++ templates type-deduction
我想创建一个简单的辅助算法来填充容器,例如std::vector<T>,具有几何级数(第一个术语是a,n第 - 个术语由给定的比率给出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;
}
Run Code Online (Sandbox Code Playgroud)
尝试编译时产生以下错误:
$ 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’
Run Code Online (Sandbox Code Playgroud)
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.
Run Code Online (Sandbox Code Playgroud)
我原本以为使用模板模板参数我不仅可以推断容器,还可以推断容器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;
}
Run Code Online (Sandbox Code Playgroud)
输出:
10
8
6.4
5.12
4.096
3.2768
2.62144
2.09715
1.67772
1.34218
Run Code Online (Sandbox Code Playgroud)
第一个问题是,您忘记了这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) {
// ^^^^
// ...
}
Run Code Online (Sandbox Code Playgroud)
请注意,这将使得无法传递(例如)第一个函数std::map或std::unordered_map第一个函数参数的实例.因此,我的建议是放弃推断第一个参数是标准容器的实例(标准容器不是那么统一):
template<typename C, typename T, typename A>
// ^^^^^^^^^
void progression(C& container, T a, T ratio, size_t N) {
// ^^
// ...
}
Run Code Online (Sandbox Code Playgroud)
您可能想要做的是表达编译时约束,可能通过static_assert基于自定义类型特征,并且C必须是标准容器的实例.
或者,您可以在他的答案中使用KerrekSB建议的可变参数模板(但这仍然不会阻止您传入任何其他类型的模板的实例,甚至是非容器模板的实例).
在第二个问题是,你在呼唤你的模板的方式:
progression(r, 10, 0.8, 10);
Run Code Online (Sandbox Code Playgroud)
这里,第二个参数int的类型是,而容器元素的类型是double.执行类型推导时,这会使编译器感到困惑.要么这样称呼它:
progression(r, 10.0, 0.8, 10);
Run Code Online (Sandbox Code Playgroud)
或者允许编译器为第二个参数推导出不同的类型(可能SFINAE - 将其限制为可以转换为元素类型的东西).