我想知道在C ++中我们应该在functor上使用 lambda 表达式。对我来说,这两种技术基本相同,甚至算子也比 lambda 更优雅,更清洁。例如,如果我想重用我的谓词,我必须一遍又一遍地复制lambda部分。那么lambda什么时候真的进入呢?
答案 0 :(得分:16)
lambda表达式创建了一个无名函子,它是语法糖。
所以你主要使用它,如果它让你的代码看起来更好。如果(a)你不打算重用仿函数,或者(b)你要重用它,但是从代码中完全与当前代码完全无关,为了分享它,你通常会发生这种情况。最终创建my_favourite_two_line_functors.h
,并且不同的文件依赖于它。
几乎与您键入任何代码行的条件相同,而不是将代码块抽象为函数。
也就是说,使用C ++ 0x中的range-for语句,在某些地方你可能会使用仿函数,之后它可能会使代码看起来更好,现在将代码编写为循环体,而不是仿函数或lambda。
答案 1 :(得分:15)
1)这是微不足道的,试图分享它是更多的工作而不是利益。
2)定义一个仿函数只会增加复杂性(由于必须制作一堆成员变量和废话)。
如果这两件事都不对,那么也许你应该考虑定义一个仿函数。
编辑:似乎你需要一个例子,说明在一个仿函数上使用lambda会很好。你走了:
typedef std::vector< std::pair<int,std::string> > whatsit_t;
int find_it(std::string value, whatsit_t const& stuff)
{
auto fit = std::find_if(stuff.begin(), stuff.end(), [value](whatsit_t::value_type const& vt) -> bool { return vt.second == value; });
if (fit == stuff.end()) throw std::wtf_error();
return fit->first;
}
如果没有lambdas,你必须使用类似地在现场构造一个仿函数的东西,或者编写一个外部可链接的仿函数对象来处理那些令人烦恼的事情。
顺便说一下,我想也许wtf_error是一个扩展名。答案 2 :(得分:11)
Lambdas 基本只是实现仿函数的语法糖(NB:闭包并不简单。)在C ++ 0x中,你可以使用auto关键字在本地存储lambda,而std :: function将使您能够存储lambda,或以类型安全的方式传递它们。
答案 3 :(得分:8)
不重复的小功能。
关于仿函数的主要抱怨是它们与使用它们不在同一个地方。因此,您必须在上下文中找到并阅读仿函数(即使它只在一个地方使用)。
另一个问题是仿函数需要一些布线来将参数引入仿函数对象。并不复杂,但所有基本的样板代码。锅炉板容易出现切割和粘贴问题。
Lambda尝试并修复这两个问题。但是如果函数在多个地方重复或者大于(不能想出适当的术语,因为它将是上下文敏感的),我会使用仿函数。
答案 4 :(得分:1)
#include <iostream>
#include <list>
#include <vector>
using namespace std;
//Functions have no context, mod is always 3
bool myFunc(int n) { return n % 3 == 0; }
//Functors have context, e.g. _v
//Functors can be more complex, e.g. additional addNum(...) method
class FunctorV
{
public:
FunctorV(int num ) : _v{num} {}
void addNum(int num) { _v.push_back(num); }
bool operator() (int num)
{
for(int i : _v) {
if( num % i == 0)
return true;
}
return false;
}
private:
vector<int> _v;
};
void print(string prefix,list<int>& l)
{
cout << prefix << "l={ ";
for(int i : l)
cout << i << " ";
cout << "}" << endl;
}
int main()
{
list<int> l={1,2,3,4,5,6,7,8,9};
print("initial for each test: ",l);
cout << endl;
//function, so no context.
l.remove_if(myFunc);
print("function mod 3: ",l);
cout << endl;
//nameless lambda, context is x
l={1,2,3,4,5,6,7,8,9};
int x = 3;
l.remove_if([x](int n){ return n % x == 0; });
print("lambda mod x=3: ",l);
x = 4;
l.remove_if([x](int n){ return n % x == 0; });
print("lambda mod x=4: ",l);
cout << endl;
//functor has context and can be more complex
l={1,2,3,4,5,6,7,8,9};
FunctorV myFunctor(3);
myFunctor.addNum(4);
l.remove_if(myFunctor);
print("functor mod v={3,4}: ",l);
return 0;
}
输出:
initial for each test: l={ 1 2 3 4 5 6 7 8 9 }
function mod 3: l={ 1 2 4 5 7 8 }
lambda mod x=3: l={ 1 2 4 5 7 8 }
lambda mod x=4: l={ 1 2 5 7 }
functor mod v={3,4}: l={ 1 2 5 7 }
答案 5 :(得分:0)
正如您所指出的,当您需要一次性时它最有效,并且将其写为函数的编码开销不值得。
答案 6 :(得分:0)
从概念上讲,决定使用哪个是由与使用命名变量与就地表达式或常量相同的标准驱动的......
size_t length = strlen(x) + sizeof(y) + z++ + strlen('\0');
...
allocate(length);
std::cout << length;
...在这里,创建一个长度变量可以鼓励程序考虑它的正确性和意义,而不是在以后使用它。希望这个名称足够传达,可以直观地理解它,并且可以独立于它的初始值。然后它允许多次使用该值而不重复表达式(处理z
时不同)。在这里......
allocate(strlen(x) + sizeof(y) + z++ + strlen('\0'));
...减少总代码,并将值本地化到需要的位置。从这一行读取“前进”的唯一方法是分配和增量(z)的副作用,但没有额外的局部变量与范围或以后用于考虑。程序员必须在继续分析代码的同时精神上处理较少的状态。
同样的区别适用于函数与内联语句。为了回答你的问题,函子与lambdas只是这个函数的一个特例,而不是内联决策。
答案 7 :(得分:0)
首先,我想在这里清除一些杂物。
有两件事
通常,Lambda表达式(即[] () {} -> return-type
)并不总是合成为闭包(即某种函子)。尽管这取决于编译器。但是您可以通过在+
之前将[]
强制为+[] () {} -> return-type
来强制编译器。这将创建函数指针。
现在,提您的问题。您可以重复使用lambda,如下所示:
int main()
{
auto print = [i=0] () mutable {return i++;};
cout<<print()<<endl;
cout<<print()<<endl;
cout<<print()<<endl;
// Call as many time as you want
return 0;
}
考虑到代码的可表达性和易维护性,您应该在其想到的任何地方都使用Lambda,就像可以在自定义删除器中将其用于智能指针以及大多数STL算法一样。
如果将Lambda与constexpr
,variadic template parameter pack
或generic lambda
之类的其他功能结合使用。您可以实现许多目标。
您可以找到有关here的更多信息