2013-04-17 26 views
8
#include <iostream> 

void foo(int k) { 
    static auto bar = [&]{ 
     std::cout << k << std::endl; 
    }; 
    bar(); 
} 

int main() { 
    foo(1); foo(2); foo(3); // output is correct: 1, 2, 3 
} 

Kiểm tra chức năng foo, một cách thức lambda tĩnh được chụp k bằng cách tham khảo. Điều này dường như hoạt động và điều tương tự cũng xảy ra với các kiểu dữ liệu phức tạp hơn thay vì int.Reference bị bắt trong định nghĩa biến tĩnh

Điều này có được mong đợi không? Có bất kỳ đảm bảo nào rằng địa chỉ của k sẽ giống nhau cho mọi yêu cầu foo hoặc là UB này?

Cảm ơn trước, và xin lỗi nếu điều này đã được trả lời trước đó (tôi đã cố gắng tìm một câu hỏi tương tự nhưng không thành công)

+0

Adam bạn đã bỏ lỡ điểm đó một dặm, tôi sợ – sehe

Trả lời

4

Đó là Không xác định hành vi.

mỗi khoản 5.2.2/4 của C++ 11 tiêu chuẩn về các biểu thức chức năng gọi và khởi tạo các thông số của họ:

[...] Thời gian tồn tại của một tham số kết thúc khi chức năng trong đó được xác định trả về. Việc khởi tạo và hủy của mỗi tham số xảy ra trong ngữ cảnh của hàm gọi . [...]

Do đó, lambda của bạn sẽ lưu trữ tham chiếu trở nên lơ lửng ngay khi hàm trả về cuộc gọi hàm.

Trong trường hợp này, việc triển khai thực hiện miễn phí (và có khả năng) để tạo tham số chức năng tại cùng một địa chỉ cho mỗi cuộc gọi hàm, đó có thể là lý do tại sao bạn đang quan sát đầu ra dự kiến.

Tuy nhiên, hành vi này không được bắt buộc theo tiêu chuẩn - do đó, bạn không nên dựa vào nó (nếu trường hợp này xảy ra, mã của bạn sẽ hợp pháp vì 3.8/7).

+0

Câu trả lời hay! Điều gì nói 3.8/7? –

+1

@ JoséManuel: "* Nếu, sau khi thời gian tồn tại của đối tượng đã kết thúc và trước khi lưu trữ đối tượng bị chiếm dụng hoặc phát hành , đối tượng mới được tạo tại vị trí lưu trữ mà đối tượng gốc chiếm đóng, con trỏ nhọn đối tượng gốc, một tham chiếu được gọi đến đối tượng gốc, hoặc tên của đối tượng gốc sẽ tự động tham chiếu đến đối tượng mới và, khi vòng đời của đối tượng mới đã bắt đầu, có thể sử dụng để thao tác đối tượng mới , nếu * [...] "- Và sau đây là những điều kiện được thỏa mãn bằng ví dụ của bạn. –

+0

* "Việc triển khai miễn phí (và có khả năng) để tạo tham số chức năng tại cùng một địa chỉ cho mỗi cuộc gọi hàm" * - Tất nhiên, nhưng không thực sự * "có khả năng" *, xem xét chúng thường được chuyển vào ngăn xếp (có địa chỉ hàng đầu rất * không * sẽ giống nhau mỗi lần hàm được gọi). Hoặc bạn đã chỉ có nghĩa là trong ví dụ của mình, nơi nó thực sự * "có khả năng" * rằng con trỏ ngăn xếp sẽ không thay đổi giữa các cuộc gọi chức năng cá nhân? –

1

Lý do có thể là "hoạt động" trong ví dụ của bạn là ngăn xếp cuộc gọi luôn xếp hàng theo cùng một cách. Hãy thử thay vào đó và xem liệu bạn có nhận được kết quả "mong đợi" hay không.

#include <iostream> 

void foo(int k) { 
    static auto bar = [&]{ 
     std::cout << k << std::endl; 
    }; 
    bar(); 
} 

void baz(int k) { 
    std::cout << "baz: "; 
    foo(k); 
} 

int main() { 
    foo(1); baz(2); foo(3); 
} 
+0

Tôi đang sử dụng clang3.2 và g ++ 4.7.2 và phiên bản của bạn dường như cũng "hoạt động". Nhưng tôi chỉ cố gắng thay thế * foo (k) * của bạn với * foo (k + 1) * và đoán xem, bây giờ tôi có các kết quả đầu ra khác nhau từ hai trình biên dịch :) (clang: 1, 2, 3 và gcc: 1 , 3, 3) –

+1

Điểm không phải là trình biên dịch bạn đang sử dụng; vấn đề là hành vi không xác định. Điều đó có nghĩa là nó có thể hoạt động trong một số trường hợp và gặp sự cố trong một trường hợp khác. Thậm chí tệ hơn, nó có thể định dạng ổ đĩa cứng của bạn, ngừng cung cấp oxy của bạn hoặc gây ra thiệt hại khác. –

+1

Tôi nghĩ bạn cần thêm [một vài tham số nữa] (http://ideone.com/3B37yt) để làm việc này. –

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