【问题标题】:Expand parameter pack in std::function template在 std::function 模板中展开参数包
【发布时间】:2014-08-05 07:44:58
【问题描述】:

我正在研究一些速记函数式编程方法来帮助用 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},

这可以通过直接指定&lt;void(double,double,double)&gt; 而不是&lt;void(T,Ts...)&gt;std::function 来实现,但这显然不是一个有用的修复。代码编译失败,错误与模板参数扣除/替换有关:

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

所以我的直觉是,出于某种原因,Ts 没有被扩展......我有任何指示或明显的疏忽吗?一般来说,我对模板函数很陌生,因此感谢您提供任何帮助。

【问题讨论】:

  • 这很有趣,得到的错误是error: 'cout' was not declared in this scope
  • 这里不需要使用std::function,直接将仿函数的类型作为模板参数即可:template&lt;typename F, typename T, typename... Ts&gt; void mapthreadv(F func, /*...*/)
  • 另外,rangerange(0.0, 1.0, 0.3) 返回了太多元素,因为 0.3 不能均匀地划分为 1.0。不幸的是,我想不出一个适用于双精度和整数的简单修复方法。连double都在自找麻烦。
  • 我忘记在我的原始帖子中包含using namespace std;

标签: c++ c++11


【解决方案1】:

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

模板参数推导可以扩展模板的序列 对应于模板参数包的参数,即使当 序列包含明确指定的模板参数。 [ 例子

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.

【讨论】:

  • 太棒了。回想起来似乎很明显。谢谢!
  • 其实用这个方法是不是可以把有问题的函数的返回类型捕获为模板参数呢?
  • @BenLand100 您不需要将其捕获为模板参数 - 只需使用 decltype(func(in[0], rest[0]...)) 之类的东西。
  • @BenLand100 如果你只想要依赖于模板参数的东西,那么std::declval 是你的朋友:decltype(std::declval&lt;F&gt;()(std::declval&lt;T&gt;(), std::declval&lt;Ts&gt;()...))
猜你喜欢
  • 2021-10-30
  • 1970-01-01
  • 2014-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多