在std :: function模板中展开参数包

时间:2014-08-04 22:16:37

标签: c++ c++11

我正在研究一些速记函数式编程方法,以帮助用C ++进行数据分析,我遇到了一种情况,我觉得我的实施工作应该有效,但g ++不同意我的看法。请参阅以下代码:

#include <algorithm>
#include <valarray>
#include <functional>
#include <iostream>

using namespace std;

//generates a list of [from,to] in increments of step. last is <= to with precision of step
template<typename T> std::valarray<T> range(T from, T to, T step = 1) {
    size_t elems = (size_t)floor((to-from)/step) + 1;
    std::valarray<T> result(elems);
    for (int i = 0; i < elems; i++) {
        result[i] = from+step*i;
    }
    return result;
}

//map over multiple lists as arguments to the provided function
template<typename T, typename... Ts> void mapthreadv(std::function<void(T,Ts...)> func, std::valarray<T> &in, std::valarray<Ts>&... rest) {
    for (int i = 0; i < in.size(); i++) {
        func(in[i],rest[i]...);
    }
}

int main(int argc, char **argv) {  
    auto first = range(0.0,1.0,0.1);
    auto second = range(0.0,10.0,1.0);
    auto third = range(0.0,100.0,10.0);
    mapthreadv<double,double,double>([](double a, double b, double c) { cout << '{' << a << ',' << b << ',' << c << "},"; },first,second,third);
}   

预期输出为:

{0,0,0},{0.1,1,10},{0.2,2,20},{0.3,3,30},{0.4,4,40},{0.5,5,50},{0.6,6,60},{0.7,7,70},{0.8,8,80},{0.9,9,90},{1,10,100},

可以通过直接指定<void(double,double,double)>代替<void(T,Ts...)>std::function来实现,但这显然不是一个有用的修复方法。代码无法按编写方式编译,错误与模板参数推断/替换有关:

‘main(int, char**)::<lambda(double, double, double)>’ is not derived from ‘std::function<void(double, Ts ...)>’

所以我的直觉是,由于某种原因,Ts没有被扩展......我的任何指针或明显的疏忽?我对模板功能一般都很陌生,所以感谢任何帮助。

1 个答案:

答案 0 :(得分:2)

问题是当您使用模板参数包时仍然会执行模板参数推导,即使您明确指定了类型(第14.8.1节[temp.arg.explicit] / p9):

  

模板参数推导可以扩展模板的顺序   对应于模板参数包的参数,即使是   sequence包含显式指定的模板参数。 [   实施例

template<class ... Types> void f(Types ... values);
void g() {
    f<int*, float*>(0, 0, 0);
}
// Types is deduced to the sequence int*, float*, int
     

- 结束示例]

并且,由于lambda闭包类型不是std::function,模板参数推断将失败。

无论如何都没有理由在这里使用std::function;您可以简单地将仿函数作为模板参数:

template<typename F, typename T, typename... Ts> void mapthreadv(F func, std::valarray<T> &in, std::valarray<Ts>&... rest) {
    for (int i = 0; i < in.size(); i++) {
        func(in[i],rest[i]...);
    }
}

也避免了明确指定模板参数的需要:

mapthreadv([](double a, double b, double c) { std::cout << '{' << a << ',' << b << ',' << c << "},"; },first,second,third);

Demo