2015-10-01 13 views
5

Hãy xem xét các chương trình đơn giản:Lambda chụp tài liệu tham khảo bằng cách sao chép và decltype

int i = 0; 
int& j = i; 

auto lambda = [=]{ 
    std::cout << &j << std::endl; //odr-use j 
}; 

Theo [expr.prim.lambda], thành viên đóng cửa biến j cần phải có loại int:

Một thực thể là được chụp bằng bản sao nếu được chụp hoàn toàn và chụp mặc định= hoặc nếu được chụp rõ ràng bằng ảnh chụp không có dạng & số nhận dạng hoặc & trình khởi tạo nhận dạng. Đối với mỗi thực thể được chụp bởi bản sao, một thành viên dữ liệu không có tên chưa được đặt tên được khai báo trong loại đóng. Thứ tự khai báo của các thành viên này không được chỉ định. Loại thành viên dữ liệu như vậy là loại đối tượng được ghi tương ứng nếu đối tượng không phải là tham chiếu đến đối tượng, hoặc loại được tham chiếu khác.

Vì vậy, những gì tôi in là địa chỉ của một số int không liên quan đến bên ngoài phạm vi-i hoặc j. Đây là tất cả tốt và tốt. Tuy nhiên, khi tôi ném vào decltype:

auto lambda = [j] { 
    std::cout << &j << std::endl; 
    static_assert(std::is_same<decltype(j), int>::value, "!"); // error: ! 
}; 

Đó không biên dịch vì decltype(j) đánh giá như int&. Tại sao? j trong phạm vi đó nên tham khảo thành viên dữ liệu, phải không?

Là một followup liên quan, nếu chụp lambda là thay vì một init-chụp với [j=j]{...}, sau đó kêu vang sẽ báo cáo decltype(j) như int và không int&. Tại sao sự khác biệt?

+0

bắt đầu bằng '[j = j]' có nghĩa là khoảng 'auto j = j' bên trái' j' không phải là ảnh chụp vì vậy không có gì ngạc nhiên khi nó có ngữ nghĩa khác nhau. init-capture với '[j]' sẽ là tương đương với bạn đang tìm kiếm. – Guvante

+1

"nên tham khảo thành viên dữ liệu". Chỉ khi odr được sử dụng. Nếu không, nó đề cập đến thực thể ban đầu. –

Trả lời

9

Các tra cứu tên cách làm việc bên trong lambda-biểu thức được một chút đặc biệt: id-biểu mà tham khảo cho các tổ chức bị bắt bằng cách sao chép được chuyển đổi từ các truy cập tới các đối tượng bị bắt để truy cập đến các thành viên dữ liệu lưu trữ của việc đóng cửa loại - nhưng chỉ khi những truy cập này cấu thành odr-use. Lưu ý rằng do nắm bắt tiềm ẩn, nếu không có sử dụng odr, có thể không có thành viên dữ liệu nào như vậy.

decltype không cấu thành một sử dụng odr, do đó nó sẽ luôn luôn tham chiếu đến thực thể bị bắt (bản gốc), không phải là thành viên dữ liệu (bản sao).

C++ 11 [expr.prim.lamba] P17

Mỗi id biểu hiện đó là một ODR sử dụng của một thực thể bị bắt bởi bản sao được chuyển thành một quyền truy cập vào các giấu tên tương ứng dữ liệu thành viên của loại đóng cửa.

và hơn nữa, P18 thậm chí hiển thị hiệu ứng kỳ lạ này trong một ví dụ:

Mỗi lần xuất hiện của decltype((x)) nơi x là một thể ngoặc id-biểu rằng tên một thực thể thời hạn lưu trữ tự động là được xử lý như thể x được chuyển thành quyền truy cập vào một thành viên dữ liệu tương ứng của loại đóng cửa đã được khai báo nếu x là một sử dụng thực thể được ký hiệu. [Ví dụ:

void f3() { 
    float x, &r = x; 
    [=] { // x and r are not captured (appearance in a decltype operand is not an odr-use) 
     decltype(x) y1;  // y1 has type float 
     decltype((x)) y2 = y1; // y2 has type float const& because this lambda 
           // is not mutable and x is an lvalue 
     decltype(r) r1 = y1; // r1 has type float& (transformation not considered) 
     decltype((r)) r2 = y2; // r2 has type float const& 
    }; 
} 

- cuối dụ]


C++ 14 init-chụp cũng được coi là chụp bằng cách sao chép, vì C++ 14 [expr.prim.lambda] p15

Một thực thể được bị bắt bằng cách sao chép nếu nó mặc nhiên được chụp và chụp mặc định= hoặc nếu nó được chụp một cách rõ ràng với một chụp mà không có dạng &định danh hoặc &định danh initializer .

Tuy nhiên, như T.C. đã chỉ ra, họ không nắm bắt được thực thể mà họ đã được khởi tạo với, mà là một "biến giả" mà cũng được sử dụng cho loại trừ [expr.prim.lambda] P11

một init-chụp cư xử như thể nó tuyên bố và dứt khoát chụp một biến dạng “auto init-chụp ;” mà khu vực khai báo là các lambda-biểu 's hợp chất-tuyên bố [. . .]

Loại khấu trừ sẽ thay đổi loại biến này, ví dụ: char const[N] ->char const* và thực thể gốc thậm chí có thể không có loại, ví dụ: [i = {1,2,3}]{}.

Do đó, id-biểuj trong lambda [j=j]{ decltype(j) x; } đề cập đến biến giả này và loại của nó là int, không int&.

+0

Tôi đoán rằng điều này có thể gây ngạc nhiên nếu các kiểu thay đổi bằng cách thêm vào sử dụng odr, do đó, hai lựa chọn cơ bản là: cho phép 'decltype' luôn tham chiếu đến bản gốc, hoặc để nó luôn là kiểu bản sao sẽ có . Lưu ý chắc chắn tôi thích giải pháp của họ, mặc dù. – dyp

+0

Tôi nghĩ bạn đúng, nhưng có vẻ khó hiểu. – Barry

+1

'[j = j]' là một ảnh chụp bởi bản sao, nhưng nó nắm bắt 'tự động j' trong' auto j =/* int & */ j; '(yay cho không thể viết-trong-bình thường-C++ - công cụ) , là 'int', thay vì' int & 'j. Xem [expr.prim.lambda]/p11. –

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