2016-08-29 35 views
57

试图了解lambda表达式在C++,我不明白的是:试图了解lambda表达式

int multiplier = 5; 
auto timesFive = [multiplier](int a) { return a * multiplier; }; 
std::cout << timesFive(2) << '\n'; // Prints 10 

multiplier = 15; 
std::cout << timesFive(2) << '\n'; // Still prints 2*5 == 10 (???) - Should it be 30? 

当程序调用timesFive()第二次,我希望得到的结果是30,但为何结果Still prints 2*5 == 10,而不是prints 2*15 == 30?即使我们已经试图捕获它,也许lambda函数无法跟踪multiplier的值。

而获得理想效果的方法是什么?

+5

请使标题描述的问题。是的,你试图理解lambda,但是这并不能告诉我们你实际要求的东西。 (我正在考虑自己编辑它,但我不喜欢我想出来的想法。) – jpmc26

回答

91

您通过值捕获multiplier,这意味着它被复制到lambda中。您需要通过参考采集它:

int multiplier = 5; 
auto timesFive = [&multiplier](int a) { return a * multiplier; }; 
std::cout << timesFive(2); 

multiplier = 15; 
std::cout << timesFive(2); 
+64

另外,如果你真的想要这种行为,'timesFive'这个名字不仅仅是有点误导性 –

+0

@SteveCox我很漂亮当然,这只是一个显示概念的学术范例。我们也可以称之为foo,foobar,......当然你是对的,如果那是一个真实世界的例子。 – exilit

43

兰姆达斯是不可变类的合成糖及其实例。有时将代码扩展到这个不可理解的类可以帮助理解正在发生的事情。

[ capture_list ](arg_list) -> return_value_clause_opt { body }; 

变得非常粗略地(伪代码):

struct anonymous_type { 
    capture_list; 
    auto operator()(arg_list) const -> return_value_clause_opt { 
    body 
    } 
    anonymous_type(capture_list_in):capture_list(capture_list_in) {} 
}; 

如果通过其原名列出capture_list一个变量,它是复制到匿名类中的一个副本。

所以你timesFive成为

struct __secret_name__ { 
    int multiplier; 
    int operator()(int a) const { return a*multiplier; } 
}; 
int multiplier = 5; 
auto timesFive = __secret_name__{multiplier}; 

它应该很清楚的是,在上面的代码改变multiplier不会改变timesFive行为。

如果您在名称前加上&,则非const参考被放置在匿名类中。

struct __secret_name__ { 
    int& multiplier; 
    int operator()(int a) const { return a*multiplier; } 
}; 
int multiplier = 5; 
auto timesFive = __secret_name__{multiplier}; 

现在,改变multiplier将改变timesFive的行为,因为timesFive持有参考乘法器,而不是它的复制品。


为简洁起见,上面略去了一些细节。名称__secret_name__仅用于说明。 lamba的成员变量实际上并不公开。即使其数据是可执行定义的,也是可以构造的lambda。等

+1

良好的洞察力。很久以前,我们有Cfront,由Bjarne Stroustrup提供,以获得C++的“内部工作”。这几天有什么相似之处吗?你是如何猜测你的伪代码的?直觉? – blackpen

+2

@black该标准描述了lambda是什么。它不像“('''循环)那样”as-if“,但它非常明确。 – Yakk