Loại trừ trường hợp tầm thường mà bạn nắm bắt một cách rõ ràng nội dung không được đề cập trong lambda, có hai trường hợp góc có thể có sự khác biệt.
Đầu tiên, các ảnh chụp ngầm ẩn thường không nắm bắt các thực thể không được sử dụng không đúng (nhưng xem mục tiếp theo cho ngoại lệ cho điều này). Điều này bao gồm, trong số những thứ khác, những điều đề cập trong toán hạng unevaluated như những decltype
và sizeof
, cũng như một số const
và constexpr
biến địa phương khi được sử dụng trong những bối cảnh nhất định (tham khảo ý kiến [basic.def.odr] cho các thiết lập đầy đủ các quy tắc):
void f(int){}
void f2(const int &) {}
void t() {
const int x = 8;
constexpr double y = 8;
const double z = 8;
auto g = []{ f(x); }; // OK
auto g1 = [=]{ f(x); }; // OK, does not capture x
// usually won't fire, though not guaranteed
static_assert(sizeof(g) == sizeof(g1), "!!");
auto g2 = []{ f(y); }; // OK
auto g3 = []{ f(z); }; // Error, must capture z
auto g4 = []{ f2(x); }; // Error, must capture x since it's odr-used
auto g5 = [=]{ f2(x); }; // OK, captures x
auto g6 = []{ f2(+x); }; // OK, doesn't odr-use x
auto g7 = []{ f2(y); }; // OK
}
Nếu bạn viết danh sách chụp theo cách thủ công, có thể bạn sẽ nắm bắt được nhiều hơn bạn cần về mặt kỹ thuật, bởi vì các quy tắc quản lý những gì được sử dụng hoặc không được sử dụng là khá phức tạp.
Thứ hai, các ảnh chụp ngầm trong các lambdas chung sẽ chụp những thứ được sử dụng trong một biểu thức phụ thuộc, ngay cả khi chúng không nhất thiết phải được sử dụng không đúng, cho sự tỉnh táo. Vay an example from the standard:
void f(int, const int (&)[2] = {}) { } // #1
void f(const int&, const int (&)[1]) { } // #2
void test() {
const int x = 17;
auto g2 = [=](auto a) {
int selector[sizeof(a) == 1 ? 1 : 2]{};
f(x, selector); // OK: is a dependent expression, so captures x
// Will actually odr-use x only if sizeof(a) == 1
};
}
Tuy nhiên, bạn không thực sự cần thiết để nắm bắt cái gì đó có thể-hoặc-thể-không-thể-ODR được sử dụng khi viết một lambda chung; bạn chỉ bắt buộc phải nắm bắt nó nếu bạn khởi tạo một chuyên môn của toán tử cuộc gọi hàm mà không sử dụng điều đó. Và vì vậy nếu bạn không bao giờ gọi lambda chung kết quả theo cách mà sẽ sử dụng điều đó ở vấn đề, thì việc nắm bắt tiềm ẩn có thể chiếm nhiều hơn mức tối thiểu bạn cần. Ví dụ, điều này được phép:
void test() {
const int x = 17;
auto g3 = [](auto a) { // no capture
int selector[sizeof(a) == 1 ? 1 : 2]{};
f(x, selector); // OK for now
};
}
miễn là bạn đừng gọi g3
với bất cứ điều gì có kích thước là 1: g3(0)
là OK trên các hệ thống điển hình; g3('\0')
thì không.
Nếu lambda lười biếng kết thúc việc nắm bắt các biến tương tự như lambda pedantic, tôi thấy không có lý do tại sao hiệu suất hoặc thậm chí các mã máy được tạo nên khác nhau. – alain
IDK nếu nó được cho phép theo tiêu chuẩn hay không nhưng tôi nghĩ rằng trình biên dịch sẽ chỉ nắm bắt các biến mà bạn sử dụng. – NathanOliver
Sự khác biệt hoàn toàn là cú pháp, không phải ngữ nghĩa. Cả hai lambdas làm điều tương tự. –