简体   繁体   English

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

[英]A problem with higher order functions and lambdas in C++0x

I have a program where I must print many STL vectors on the screen after doing some calculation on each component.我有一个程序,在对每个组件进行一些计算后,我必须在屏幕上打印许多 STL 向量。 So I tried to create a function like this:所以我尝试像这样创建一个 function :

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

And then use it like this:然后像这样使用它:

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

Unfortunately, I'm having a compiling error about the type of the lambda expression I've put inside the printWith call:不幸的是,我在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

Of course, if I do:当然,如果我这样做:

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

then printWith(foo, sumOne);然后printWith(foo, sumOne); works as intended.按预期工作。 I thought the type of a lambda expression would be the type of a function with the inferred return type.我认为 lambda 表达式的类型将是具有推断返回类型的 function 的类型。 I also though that I could fit a lambda anywhere I could fit a normal function.我也认为我可以在任何可以安装普通 function 的地方安装 lambda。 How do I make this work?我该如何进行这项工作?

The following works for me:以下对我有用:

#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;
}

Testing:测试:

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

Alternatively, you can exploit the fact that closure types with no lambda-capture can be implicitly converted to function pointers.或者,您可以利用没有 lambda 捕获的闭包类型可以隐式转换为 function 指针的事实。 This is closer to your original code and also cuts down on the number of instantiations of the function template (in the original solution you get a new instantiation every time you use the function template with a different function object type; note though that it doesn't matter much in this specific case since the printWith function is very short and most probably will be always inlined): This is closer to your original code and also cuts down on the number of instantiations of the function template (in the original solution you get a new instantiation every time you use the function template with a different function object type; note though that it doesn'在这种特定情况下无关紧要,因为printWith function 非常短,很可能总是内联):

#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;
}

Unfortunately, implicit conversion doesn't play very well with template argument deduction: as you can see, I had to specify template arguments in the call to printWith .不幸的是,隐式转换在模板参数推导中表现不佳:如您所见,我必须在调用 printWith 时指定模板printWith

Another alternative is to use std::function .另一种选择是使用std::function This also helps to minimize the number of template instantiations and works even for lambda expressions with lambda-capture, but has the same problems with template argument deduction:这也有助于最大限度地减少模板实例化的数量,甚至适用于具有 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;
}

The reason that you're having a problem is because you're trying to use a function.您遇到问题的原因是因为您尝试使用 function。 Free functions have a specific representation (as a function pointer) which is not interchangable with function objects of any kind.自由函数具有特定的表示(作为 function 指针),它不能与任何类型的 function 对象互换。 Function pointers (which is basically what you have here) should be avoided.应该避免使用 Function 指针(基本上就是你在这里所拥有的)。 You need to take a function object directly with it's type specified by template.您需要使用模板指定的类型直接获取 function object。

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

Alternatively, take a polymorphic function object such as std::function .或者,采用多态 function object 例如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) << " "; });
}
void printWith(vector<a> foo, b func(a)){

This is wrong, you can't do that and that makes the compiler not taking account of this code as it's not valid.这是错误的,你不能这样做,这会使编译器不考虑这段代码,因为它是无效的。

You have two ways to fix this:您有两种方法可以解决此问题:

1) don't ask for a parameter type, just ask for a functor: 1)不要求参数类型,只要求函子:

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

The rest of your function will not compile if func don't take a a as parameter anyway.如果 func 不采用a作为参数,则 function 的 rest 将无法编译。

2) force the functor type: 2)强制函子类型:

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

Then it's like if you were using a function pointer.然后就像您使用的是 function 指针一样。 No (or less) compile-time optimization, but at least you enforce the functor signature.没有(或更少)编译时优化,但至少您强制执行函子签名。 See std::function or boost::function for details.有关详细信息,请参见 std::function 或 boost::function。

The reason this doesn't work is that you're mixing template argument deduction with implicit conversions.这不起作用的原因是您将模板参数推导与隐式转换混合在一起。 If you get rid of deduction it works:如果你摆脱扣除它的工作原理:

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

However, it would be better (inside printWith) to let func 's type be another template parameter, as others recommend.但是,最好(在 printWith 内部)让func的类型成为另一个模板参数,正如其他人所推荐的那样。

If on the other hand you really want to add constraints to this type there are better ways to do it using SFINAE (for soft errors) or static_assert (for hard errors).另一方面,如果您真的想为这种类型添加约束,有更好的方法可以使用 SFINAE(用于软错误)或static_assert (用于硬错误)。

For instance:例如:

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

Here, is_element_printer<T, Element>::value is true iff T implicitly converts to Element (*)(Element) .在这里, is_element_printer<T, Element>::valuetrue ,当且仅当T隐式转换为Element (*)(Element) I'm only using this for illustrative purposes and I cannot recommend it for real use: there are plenty of things that could qualify as an 'element printer' in a lot of situations that are not function pointers.我仅将其用于说明目的,我不推荐将其用于实际使用:在很多情况下,有很多东西可以称为“元素打印机”,而不是 function 指针。 I'm only doing this because std::is_convertible is readily available from <type_traits> and there is no other more obvious test available.我这样做只是因为std::is_convertible很容易从<type_traits>获得,并且没有其他更明显的测试可用。 You should write your own.你应该自己写。

Then:然后:

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) << ' '; });
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM