Đối với lambda ở phạm vi khối, các biến đáp ứng các tiêu chí nhất định trong phạm vi tiếp cận có thể được sử dụng theo những cách hạn chế bên trong lambda, ngay cả khi chúng không được nắm bắt.
Nói một cách đại khái, phạm vi tiếp cận bao gồm bất kỳ biến cục bộ nào đối với hàm chứa lambda, sẽ nằm trong phạm vi tại thời điểm lambda được xác định. Vì vậy, điều này bao gồm m
và n
trong các ví dụ trên.
Cụ thể là "tiêu chí nhất định" và "cách hạn chế" (kể từ C ++ 14):
- Bên trong lambda, biến không được sử dụng odr , có nghĩa là nó không được trải qua bất kỳ hoạt động nào ngoại trừ:
- xuất hiện dưới dạng một biểu thức giá trị bị loại bỏ (
m;
là một trong những biểu thức này) hoặc
- có giá trị của nó được truy xuất.
- Biến phải là:
- A
const
, không phải volatile
số nguyên hoặc enum có trình khởi tạo là một biểu thức hằng số , hoặc
- A
constexpr
, không volatile
biến (hoặc một đối tượng con của nó)
Tài liệu tham khảo về C ++ 14: [expr.const] /2.7, [basic.def.odr] / 3 (câu đầu tiên), [expr.prim.lambda] / 12, [expr.prim.lambda] / 10.
Cơ sở lý luận cho các quy tắc này, như được đề xuất bởi các nhận xét / câu trả lời khác, là trình biên dịch cần có khả năng "tổng hợp" lambda no-capture như một hàm miễn phí độc lập với khối (vì những thứ như vậy có thể được chuyển đổi thành một con trỏ- chức năng); nó có thể làm điều này mặc dù tham chiếu đến biến nếu nó biết rằng biến sẽ luôn có cùng một giá trị hoặc nó có thể lặp lại quy trình lấy giá trị của biến độc lập với ngữ cảnh. Nhưng nó không thể làm điều này nếu biến có thể khác nhau theo thời gian hoặc nếu địa chỉ của biến là cần thiết.
Trong mã của bạn, n
đã được khởi tạo bằng một biểu thức không phải là hằng số. Do đó, n
không thể được sử dụng trong lambda mà không bị bắt.
m
được khởi tạo bởi một biểu thức hằng 42
, vì vậy nó đáp ứng "tiêu chí nhất định". Biểu thức giá trị bị loại bỏ không sử dụng biểu thức, vì vậy m;
có thể được sử dụng mà không m
bị bắt. gcc là đúng.
Tôi sẽ nói rằng sự khác biệt giữa hai trình biên dịch là clang coi là m;
sử dụng odr m
, nhưng gcc thì không. Câu đầu tiên của [basic.def.odr] / 3 khá phức tạp:
Một biến x
có xuất hiện tên như là một biểu hiện khả năng đánh giá lại ex
là ODR-sử dụng bằng cách ex
trừ khi áp dụng việc chuyển đổi giá trị trái-to-rvalue đến x
sản lượng một biểu thức hằng số mà không làm invoke bất kỳ chức năng không tầm thường, và nếu x
là một đối tượng, ex
là một phần tử của tập hợp các kết quả tiềm năng của một biểu thức e
, trong đó chuyển đổi giá trị thành giá trị được áp dụng e
hoặc e
là biểu thức giá trị bị loại bỏ.
nhưng khi đọc kỹ, nó đề cập cụ thể rằng một biểu thức giá trị bị loại bỏ không sử dụng biểu thức.
Phiên bản [basic.def.odr] của C ++ 11 ban đầu không bao gồm trường hợp biểu thức giá trị bị loại bỏ, vì vậy hành vi của clang sẽ đúng theo C ++ 11 đã xuất bản. Tuy nhiên, văn bản xuất hiện trong C ++ 14 đã được chấp nhận là Lỗi đối với C ++ 11 ( Vấn đề 712 ), vì vậy trình biên dịch nên cập nhật hành vi của họ ngay cả trong chế độ C ++ 11.
constexpr
vsconst