高阶函数和C ++ 0x中的lambdas的问题

时间:2011-06-06 15:49:24

标签: c++ c++11 higher-order-functions lambda

我有一个程序,我必须在对每个组件进行一些计算后在屏幕上打印许多STL向量。所以我尝试创建一个这样的函数:

template <typename a> 
void printWith(vector<a> foo, a func(a)){
  for_each(foo.begin(), foo.end(), [func](a x){cout << func(x) << " "; });
}

然后像这样使用它:

int main(){
  vector<int> foo(4,0);
  printWith(foo, [](int x) {return x + 1;});
  return 0;
}

不幸的是,我有一个关于我在printWith调用中放入的lambda表达式类型的编译错误:

g++ -std=gnu++0x -Wall -c vectest.cpp -o vectest.o
vectest.cpp: In function ‘int main()’:
vectest.cpp:16:41: error: no matching function for call to ‘printWith(std::vector<int>&, main()::<lambda(int)>)’
vectest.cpp:10:6: note: candidate is: void printWith()
make: *** [vectest.o] Error 1

当然,如果我这样做:

int sumOne(int x) {return x+1;}

然后printWith(foo, sumOne);按预期工作。我认为lambda表达式的类型将是具有推断返回类型的函数的类型。我也认为我可以在任何可以适合普通功能的地方使用lambda。我如何使这项工作?

4 个答案:

答案 0 :(得分:6)

以下适用于我:

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

template <typename a, typename F>
void printWith(vector<a> foo, F f){
  for_each(foo.begin(), foo.end(), [&](a x){cout << f(x) << " "; });
}

int main(){
  vector<int> foo = {1,2,3,4,5};
  printWith(foo, [](int x) {return x + 1;});
  std::cout << '\n';
  return 0;
}

测试:

$ g++-4.5 -std=gnu++0x -Wall test.cpp
$ ./a.out                            
2 3 4 5 6 

或者,您可以利用没有lambda-capture can be implicitly converted的闭包类型来实现指针。这更接近原始代码,并且还减少了函数模板的实例化数量(在原始解决方案中,每次使用具有不同函数对象类型的函数模板时,您都会获得新的实例化;请注意,它不会在这种特殊情况下很重要,因为printWith函数非常短,并且很可能总是内联的):

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

template <typename a, typename b>
void printWith(const vector<a>& foo, b f(a)){
  for_each(foo.begin(), foo.end(), [=](a x){cout << f(x) << " "; });
}

int main(){
  vector<int> foo = {1,2,3,4,5};
  printWith<int, int>(foo, [](int x) {return x + 1;});
  std::cout << '\n';
  return 0;
}

不幸的是,隐式转换在模板参数推断方面效果不佳:正如您所看到的,我必须在调用printWith时指定模板参数。

另一种方法是使用std::function。这也有助于最小化模板实例化的数量,甚至对于具有lambda-capture的lambda表达式也有效,但是模板参数推导具有相同的问题:

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

using namespace std;

template <typename a, typename b>
void printWith(const vector<a>& foo, std::function<b(a)> f){
  for_each(foo.begin(), foo.end(), [&](a x){cout << f(x) << " "; });
}

int main(){
  vector<int> foo = {1,2,3,4,5};
  int y = 1;
  printWith<int, int>(foo, [&](int x) { return x + y; });
  std::cout << '\n';
  return 0;
}

答案 1 :(得分:5)

您遇到问题的原因是您尝试使用某项功能。自由函数具有特定表示(作为函数指针),其不能与任何类型的函数对象互换。应避免使用函数指针(基本上就是这里)。您需要使用模板指定的类型直接获取函数对象。

template <typename a, typename Func> 
void printWith(vector<a> foo, Func func){
  for_each(foo.begin(), foo.end(), [&](a x){cout << func(x) << " "; });
}

或者,使用多态函数对象,例如std::function

template<typename a>
void printWith(vector<a> foo, std::function<string(const a&)> func) {
    for_each(foo.begin(), foo.end(), [&](a x) { cout << func(x) << " "; });
}

答案 2 :(得分:3)

void printWith(vector<a> foo, b func(a)){

这是错误的,你不能这样做,这使得编译器没有考虑到这段代码,因为它无效。

您有两种解决方法:

1)不要求参数类型,只需要一个仿函数:

   void printWith(vector<a> foo, b func ){ // keep the rest of the code the same

如果func不接受a作为参数,则函数的其余部分将无法编译。

2)强制仿函数类型:

template <typename a> 
void printWith(vector<a> foo, std::function< void (a) > func ){

然后就像你使用的是函数指针。没有(或更少)编译时优化,但至少你强制执行仿函数签名。有关详细信息,请参阅std :: function或boost :: function。

答案 3 :(得分:2)

这不起作用的原因是你将模板参数推导与隐式转换混合在一起。如果你摆脱扣除它的工作原理:

printWith<int>(foo, [](int x) {return x + 1;});

然而,让func的类型成为另一个模板参数会更好(在printWith内部),正如其他人推荐的那样。

另一方面,如果你真的想为这种类型添加约束,有更好的方法可以使用SFINAE(用于软错误)或static_assert(用于硬错误)。

例如:

// A constraints metafunction
template<typename T, typename Element>
struct is_element_printer
    : std::is_convertible<T, Element (*)(Element)>
{};

此处,is_element_printer<T, Element>::valuetrue iff T隐式转换为Element (*)(Element)。我只是将它用于说明目的,我不能推荐它用于实际用途:在许多不是函数指针的情况下,有很多东西可以作为“元素打印机”。我只是这样做是因为std::is_convertible可以从<type_traits>获得,并且没有其他更明显的测试可用。你应该自己写。

然后:

template<typename Container, typename Functor>
void
printWith(Container&& container, Functor&& functor)
{
    // avoid repetition
    typedef typename std::decay<Container>::type::value_type value_type;

    // Check our constraints here
    static_assert(
        std::is_element_printer<
            typename std::decay<Functor>::type,
            value_type
        >::value,
        "Descriptive error message here"
    );

    // A range-for is possible instead
    std::for_each(container.cbegin(), container.cend(), [&functor](value_type const& v)
    { std::cout << functor(v) << ' '; });
}