2013-05-31 154 views
0

我想创造一个简单的辅助算法,将填充的容器,如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; 
} 

这在尝试编译产生了以下错误:

$ 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++ 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 

回答

5

第一个问题是,你忘记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::mapstd::unordered_map的一个实例第一个函数参数。因此,我的建议是放弃推断,第一个参数是一个标准集装箱的实例(标准集装箱,只是没有那么均匀):

template<typename C, typename T, typename A> 
//  ^^^^^^^^^ 
void progression(C& container, T a, T ratio, size_t N) { 
//    ^^ 
// ... 
} 

你可能想要做的又是什么来表达编译时约束,也许通过static_assert,并基于自定义类型的特点,那C必须是一个标准集装箱的一个实例。

或者,您可以使用可变参数模板as suggested by KerrekSB in his answer(但仍然不会阻止您传入任何其他类型的模板实例,即使是非容器模板也是如此)。

第二个问题是在你调用模板的方式:

progression(r, 10, 0.8, 10); 

这里,第二个参数的类型是int,而容器元素的类型是double。这会在执行类型推断时混淆编译器。或者把它这种方式:

progression(r, 10.0, 0.8, 10); 

或让你的编译器来推断不同类型的第二个参数(可能SFINAE约束它是东西,可以转换成元素类型)。

+0

谢谢!这解决了这个问题。 – Escualo

+0

我其实有这个答案的改进版本,但我的网络连接断开,我无法从我的手机进行编辑(它只是一团糟)。 –

+0

@阿列塔:很高兴帮助! –

1

,这是失败的原因是因为你说的中间有两个参数是相同的类型,当他们实际上不是。例如,中间是一个浮点数,而另一个是一个int。基本上你说a和比例是相同的类型,但在通话中它们是不同的类型

+0

我认为你的意思是说中间是一个“双”,而前面的那个也应该是这样。 – chris

+0

看看我的编辑 - 这不是问题。即使我传递两个'double'并将整数作为size_t来投放,问题依然存在。 – Escualo

+0

@aaronman - 这实际上是一个问题 - 在我删除第一个错误之后,你描述的错误确实出现了。 – Escualo

4

容器通常有很多模板参数。幸运的是,在可变参数模板一项特别条款,允许使用使用一包的任何具体数目的参数:(特殊条款的影响,这个工程即使Container是一个非可变参数模板)

template <template <typename...> class Container, typename ...Args> 
void foo(Container<Args...> const & c) 
{ 
    // ... 
} 

+0

这不是一个可变参数模板 – aaronman

+1

@aaronman的语法:从来没有声称这是? –

+0

我从来没有想过要这样做。那很整齐。 – chris