【问题标题】:How can I pass a lambda (c++11) into a templated function?如何将 lambda (c++11) 传递给模板化函数?
【发布时间】:2011-10-31 06:49:46
【问题描述】:

我在 gcc 4.6.2 中使用 lambda 函数,并希望实现一个模板化的“map”函数,如下所示:

template<typename A, typename B> std::vector<B> map(const std::vector<A>& orig, const std::function<B(A)> f) {
  std::vector<B> rv;
  rv.resize(orig.size());
  std::transform(begin(orig), end(orig), begin(rv), f);
  return rv;
}

这不行,因为测试代码:

int main(int argc, char **argv) {
  std::vector<int> list;
  list.push_back(10);
  list.push_back(20);
  list.push_back(50);

  std::vector<int> transformed = map(list, [](int x) -> int { return x + 1; });
  std::for_each(begin(transformed), end(transformed), [](int x) { printf("-> %d\n", x); });
  return 0;
}

给出这个错误:

test.cpp:49:80: error: no matching function for call to ‘map(std::vector<int>&, main(int, char**)::<lambda(int)>)’
test.cpp:49:80: note: candidate is:
test.cpp:6:49: note: template<class A, class B> std::vector<B> map(const std::vector<A>&, std::function<B(A)>)

如果我删除模板,直接使用向量,它编译得很好:

std::vector<int> map(const std::vector<int>& orig, const std::function<int(int)> f) {
  std::vector<int> rv;
  rv.resize(orig.size());
  std::transform(begin(orig), end(orig), begin(rv), f);
  return rv;
}

所以我定义模板的方式一定有问题。

以前有人遇到过这种情况吗?我知道 lambda 是非常新的。

【问题讨论】:

  • 您知道在您帖子的示例中,您在调用map_ 时定义了函数map?注意调用中的下划线... :-)
  • 抱歉打错了——我从一个文件中粘贴了十几个不同的尝试,看看我是否可以让编译器给我一个关于它不喜欢什么的更好的线索。我想我现在已经修好了。

标签: c++ lambda c++11


【解决方案1】:

您不需要使用 std::function。只需将谓词参数设为模板值即可。例如,

template<typename A, typename B> std::vector<B> map(const std::vector<A>& orig, B f) {

std::function 作为成员值类型或定义非模板代码更有用。

【讨论】:

  • 假设您要在模板中添加一个新的“typename C”,并设置参数“C f”,这也有同样的问题。它认为“地图”功能不匹配。
【解决方案2】:

问题是编译器无法确定 B 使用什么。为了确定它想要使用函数的类型你传入f,但你没有传递一个std ::function 直接。你传入一些你希望用来构造函数的东西。为了进行这种隐式构造,它需要知道参数的类型。所以你得到了这种循环依赖,其中参数的类型取决于你传入的内容,但传入的内容取决于参数的类型。

你可以通过指定模板参数来打破这种循环依赖,比如map_&lt;int,int&gt;(list, [](int x) -&gt; char { return x + 1; });

(虽然我看到函子实际上返回一个 char,而不是一个 int,所以如果在这里类型推导对你有用,你会得到一个 vector&lt;char&gt;,当你分配结果到transformed)

然而,正如已经指出的那样,通常模板将函子视为简单的模板类型:

template<typename A,typename Func>
auto map_(const std::vector<A>& orig, Func f) -> std::vector<decltype(f(A()))> {
    std::vector<decltype(f(A()))> rv;
    /*...*/
}

(我们使用尾随返回类型,因为我们需要在返回类型中使用表达式f,除非返回类型在后面,否则该表达式不可用。)

这允许模板直接推断函子类型并避免任何类型转换并最好地进行优化。

习惯上使用迭代器作为此类函数的参数,在这种情况下,您的函数只是 std::transform 的包装器,因此您可以直接使用它。我不确定专门处理向量的特殊版本是否有很多价值。

【讨论】:

  • 行得通! (我修复了“char”的东西;对此感到抱歉。)所以听起来我错过了 2 点:1. Lambda 不是函数类型,而是使用隐式构造函数转换的。 2. 标记 lambda 类型的唯一真正方法是使用 decltype,而 lambdas 并没有任何其他方式可以描述为类型。同意这个例子毫无意义的特殊性——我只是想看看它是否可以完成,它可以!感谢您的帮助!
  • 这个问题的答案有一个使用可变参数模板的类型特征,这使得询问 lambda 的类型变得更容易。例如,代替我使用 decltype,您可以使用它来表示 function_traits&lt;Func&gt;::result_typestackoverflow.com/questions/7943525/…
  • 如果 A 不是默认可构造的,我的 decltype 也不起作用。 decltype(f(orig[0])) 是另一种选择。
【解决方案3】:

我也在处理 lambda,我注意到你可以在函数定义的参数列表中声明一个函数指针,当你调用该函数时,如果它与函数原型匹配,你可以将 lambda 表达式作为参数传递当然。

#include <iostream>

#include <vector>

#include <algorithm>

#include <iterator>



template <typename T,typename C>

struct map  {

   typedef C (*F)(const T&);

   std::vector<C> rv;

   map () {}

   map (const std::vector<T>& o,F f)  {

      rv.resize(o.size());

      std::transform (o.begin(),o.end(),rv.begin(),f);

    }

   ~map () {}

   operator std::vector<C> () const  {

      return rv;

    }

 };



int main ()  {

   std::vector<int> asd(5,12);

   std::vector<char> transformed=map<int,char>(asd,[](const int& x)->char {return x+1;});

   std::copy (transformed.begin(),transformed.end(),std::ostream_iterator<int>(std::cout," "));

 }

【讨论】:

    猜你喜欢
    • 2017-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-12
    • 2023-04-07
    • 2015-09-10
    • 1970-01-01
    相关资源
    最近更新 更多