Tôi nghĩ Clang thực sự có thể đúng.
Theo [lambda.capture] / 11 , một biểu thức id được sử dụng trong lambda chỉ đề cập đến thành viên bị bắt bởi bản sao của lambda chỉ khi nó cấu thành sử dụng odr . Nếu nó không, thì nó đề cập đến thực thể ban đầu . Điều này áp dụng cho tất cả các phiên bản C ++ kể từ C ++ 11.
Theo [basic.dev.odr] / 3 của C ++ 17, một biến tham chiếu không được sử dụng nếu sử dụng chuyển đổi lvalue sang rvalue cho nó mang lại một biểu thức không đổi.
Tuy nhiên, trong dự thảo C ++ 20, yêu cầu đối với chuyển đổi từ giá trị sang giá trị được loại bỏ và đoạn văn có liên quan đã thay đổi nhiều lần để bao gồm hoặc không bao gồm chuyển đổi. Xem vấn đề CWG 1472 và CWG phát hành 1741 , cũng như mở vấn đề CWG 2083 .
Do m
được khởi tạo với một biểu thức không đổi (tham chiếu đến một đối tượng thời lượng lưu trữ tĩnh), sử dụng nó mang lại một biểu thức không đổi cho mỗi ngoại lệ trong [expr.const] /2.11.1 .
Tuy nhiên, đây không phải là trường hợp nếu các chuyển đổi từ giá trị sang giá trị được áp dụng, bởi vì giá trị của n
không thể sử dụng được trong một biểu thức không đổi.
Do đó, tùy thuộc vào việc chuyển đổi lvalue sang rvalue có được áp dụng trong việc xác định sử dụng odr hay không, khi bạn sử dụng m
trong lambda, nó có thể hoặc không thể đề cập đến thành viên của lambda.
Nếu chuyển đổi nên được áp dụng, GCC và MSVC là chính xác, nếu không thì Clang là.
Bạn có thể thấy Clang thay đổi hành vi nếu bạn thay đổi khởi tạo m
thành không còn là biểu thức không đổi nữa:
#include <stdio.h>
#include <functional>
int n = 100;
void g() {}
std::function<int()> f()
{
int &m = (g(), n);
return [m] () mutable -> int {
m += 123;
return m;
};
}
int main()
{
int x = n;
int y = f()();
int z = n;
printf("%d %d %d\n", x, y, z);
return 0;
}
Trong trường hợp này, tất cả các trình biên dịch đồng ý rằng đầu ra là
100 223 100
bởi vì m
trong lambda sẽ đề cập đến thành viên của bao đóng có kiểu int
sao chép được khởi tạo từ biến tham chiếu m
trong f
.