2016-08-29 30 views
57

Đang cố gắng để hiểu lambdas trong C++, những gì tôi không hiểu điều này là:Đang cố gắng để hiểu lambdas

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? 

Khi chương trình gọi là timesFive() lần thứ hai, tôi mong đợi kết quả là 30. Nhưng tại sao là kết quả Still prints 2*5 == 10, không phải prints 2*15 == 30? Có lẽ chức năng lambda bằng cách nào đó không thể theo dõi giá trị của multiplier, mặc dù chúng tôi đã cố gắng nắm bắt nó?

Và cách để có được kết quả mong muốn là gì?

+5

Hãy đặt tiêu đề mô tả vấn đề. Có, bạn đang cố gắng hiểu lambdas, nhưng điều đó không cho chúng tôi biết nhiều về những gì bạn đang thực sự yêu cầu. (Tôi đang xem xét tự chỉnh sửa bản thân mình, nhưng tôi không thích những ý tưởng mà tôi đã đưa ra). – jpmc26

Trả lời

91

Bạn đã chụp multiplier theo giá trị, có nghĩa là nó đã được sao chép vào lambda. Bạn cần chụp ảnh bằng cách tham chiếu:

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

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

Ngoài ra, nếu bạn thực sự muốn hành vi này, tên 'timesFive' ​​là nhiều hơn một chút gây hiểu lầm –

+0

@SteveCox Tôi khá chắc chắn đây chỉ là một ví dụ học tập cho thấy một khái niệm. Chúng tôi cũng có thể gọi nó là foo, foobar, .... Tất nhiên bạn đúng, nếu đó là một ví dụ thực tế. – exilit

43

Lambdas là đường cú pháp cho lớp không thể đặt tên và trường hợp của chúng. Đôi khi, mở rộng mã của bạn ra ngoài với những gì lớp học không thể đặt tên này có thể giúp hiểu những gì đang xảy ra.

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

trở nên rất gần (pseudo-code):

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

Nếu bạn liệt kê một biến trong capture_list theo tên đơn giản của nó, nó là sao chép vào một bản sao trong lớp nặc danh.

Vì vậy timesFive của bạn trở thành

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

Nó nên được khá rõ ràng rằng việc thay đổi multiplier trong đoạn mã trên sẽ không thay đổi hành vi của timesFive.

Nếu bạn đặt & ở phía trước tên, tài khoản không consttham chiếu được đặt trong lớp ẩn danh.

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

bây giờ, thay đổi multiplier sẽ thay đổi hành vi của timesFive, vì timesFive nắm giữ một tài liệu tham khảo để Multiplier, không phải là một bản sao của nó.


Một số chi tiết bỏ qua ở trên để ngắn gọn. Tên __secret_name__ chỉ dành cho giải trình. Các biến thành viên của lamba không thực sự là công khai. Các lambda được trivially constructible được thực hiện được xác định ngay cả khi dữ liệu của nó được. Vv

+1

Thông tin chi tiết hữu ích. Cách đây rất lâu, chúng tôi đã có Cfront, lịch sự của Bjarne Stroustrup, để có được "hoạt động bên trong" của C++. Có gì tương tự trong những ngày này không? Bạn phỏng đoán mã giả của mình như thế nào? trực giác? – blackpen

+2

@black tiêu chuẩn mô tả lambda là gì.Nó không hoàn toàn giống như "if-if" như các vòng lặp 'for (:)', nhưng nó rất rõ ràng. – Yakk

Các vấn đề liên quan