2015-04-13 17 views
27

Hãy xem xét đoạn mã sau:Loại trả về của biểu thức lambda nếu một mục của vectơ được trả về là gì?

#include <iostream> 
#include <vector> 
#include <functional> 

int main() 
{ 
    std::vector<int>v = {0,1,2,3,4,5,6}; 
    std::function<const int&(int)> f = [&v](int i) { return v[i];}; 
    std::function<const int&(int)> g = [&v](int i) -> const int& { return v[i];}; 

    std::cout << f(3) << ' ' << g(3) << std::endl; 
    return 0; 
} 

Tôi đã chờ đợi kết quả tương tự: trong f, v được truyền bằng tham chiếu const, do đó v[i] nên có const int& loại.

Tuy nhiên, tôi nhận được kết quả

0 3 

Nếu tôi không sử dụng std :: chức năng, mọi thứ đều tốt:

#include <iostream> 
#include <vector> 
#include <functional> 

int main() 
{ 
    std::vector<int>v = {0,1,2,3,4,5,6}; 
    auto f = [&v](int i) { return v[i];}; 
    auto g = [&v](int i) -> const int& { return v[i];}; 

    std::cout << f(3) << ' ' << g(3) << std::endl; 
    return 0; 
} 

đầu ra:

3 3 

Vì vậy tôi m tự hỏi:

  1. Trong đoạn thứ hai, loại trả về của biểu thức lambda f là gì? Có phải là f giống như g?

  2. Trong đoạn đầu tiên, điều gì đã xảy ra khi std::function f được tạo, gây ra lỗi?

+0

Trình biên dịch nào? – 0x499602D2

+0

@ 0x499602D2 sử dụng ideone, C++ 14. http://ideone.com/gkAorj –

+0

Rất kỳ quặc, nó cung cấp cho [lỗi phân đoạn] (http://melpon.org/wandbox/permlink/Vnf1dCbOqHmMlBAQ) trong phiên bản gcc mới nhất. – 0x499602D2

Trả lời

5
  1. Tôi giả sử lambda đầu tiên có thể quay trở lại int hay int& ¹. Không const int& như tài liệu tham khảo v không phải là một đối tượng const (nó không quan trọng mà chụp chính nó là không thể thay đổi, vì đó là tài liệu tham khảo chính nó)

  2. lần Life of temporaries được mở rộng đến hết phạm vi kèm theo khi ràng buộc với một const-ref


¹ tôi sẽ cố gắng bổ nhào vào mà sau này. Hiện tại, tôi sẽ có trực giác khấu trừ thường xuyên và nói rằng auto sẽ hủy int, trong khi auto& sẽ khấu trừ int&, vì vậy tôi mong rằng loại trả lại thực tế là int. Nếu ai đó đập tôi, thì tốt hơn. Tôi sẽ không có thời gian cho một vài giờ

Xem các thử nghiệm được cung cấp bởi @milleniumbug: Live On Coliru

+1

Kiểm tra nhanh nói đó là 'int' http://ideone.com/kGXqcQ – milleniumbug

+0

Cảm ơn, @milleniumbug. Tâm trí nếu tôi chỉnh sửa nó? (Bạn có thể chỉnh sửa nó nếu tôi không đáp ứng). Điều đó không hoàn toàn giải thích về hành vi của trình biên dịch. _ (Arrg. Không có thời gian để tập trung) _ – sehe

+0

Độ chói của 'v' không thực sự có liên quan, suy giảm kiểu suy giảm của lambda (như trả về' tự động', như câu trả lời của TC nói) và 'int' không phân biệt cho dù 'v [i]' là const hay không. –

18

Kiểu trả về của một lambda sử dụng các loại quy tắc auto trở lại khấu trừ, dùng để tách các referenceness. (Ban đầu, nó sử dụng một bộ quy tắc hơi khác nhau dựa trên chuyển đổi từ rvalue sang rvalue (cũng đã loại bỏ tham chiếu), nhưng đã được thay đổi bởi a DR.)

Do đó, [&v](int i) { return v[i];}; trả về int. Kết quả là, trong số std::function<const int&(int)> f = [&v](int i) { return v[i];};, gọi số f() trả về một tham chiếu treo lơ lửng. Ràng buộc một tham chiếu đến tạm thời kéo dài tuổi thọ của tạm thời, nhưng trong trường hợp này sự ràng buộc xảy ra sâu bên trong máy móc của std::function, do đó, vào thời điểm f() trả về, tạm thời đã biến mất rồi.

g(3) là tốt bởi vì const int & được trả về bị ràng buộc trực tiếp với phần tử vectơ v[i], vì vậy tham chiếu không bao giờ được treo lơ lửng.

+0

g (3) trả về tham chiếu đến int trong bộ nhớ trong của vectơ, đúng không? Vì vậy, có thể rằng (về mặt lý thuyết) vector có thể tái phân bổ và làm cho tham chiếu này không hợp lệ? – barney

12

Loại trả về của biểu thức lambda nếu một mục của véc tơ được trả về là gì?

Đó là câu hỏi sai.

Bạn nên hỏi kiểu trả về của biểu thức lambda nếu nó không được chỉ định rõ ràng?.

Câu trả lời được đưa ra trong C++ 11 5.1.2 [expr.prim.lambda] đoạn 5, nơi nó nói nếu lambda không có return expr; tuyên bố nó sẽ trả về void, nếu không:

loại của biểu thức trả về sau khi chuyển đổi từ rvalue sang rvalue (4.1), chuyển đổi mảng-thành-con trỏ (4.2) và chuyển đổi hàm-to-con trỏ (4.3);

(Xem comment TC dưới đây để biết DR 1048 mà thay đổi quy tắc này một chút, và trình biên dịch thực sự thực hiện các quy tắc thay đổi, nhưng nó không quan trọng trong trường hợp này.)

vế trái-to -giá trị đổi giá trị có nghĩa là nếu câu trả về trả về một giá trị như v[i], nó phân rã thành giá trị, nghĩa là nó trả về chỉ int theo giá trị.

Vì vậy, vấn đề với mã của bạn là lambda trả về tạm thời, nhưng std::function<const int&(int)> kết thúc tốt đẹp nó gắn kết một tham chiếu đến tạm thời đó. Khi bạn cố gắng để in ra giá trị tạm thời đã aalready biến mất (nó đã được ràng buộc với một đối tượng một khung stack mà không còn tồn tại), do đó bạn có hành vi không xác định.

Khắc phục là sử dụng std::function<int(int)> hoặc đảm bảo lambda trả về tham chiếu hợp lệ, không phải là giá trị.

Trong C++ 14 phi lambdas cũng có thể sử dụng trở lại loại trừ, ví dụ:

auto f(std::vector<int>& v, int i) { return v[i]; } 

này sau tương tự (nhưng không giống hệt nhau) cai trị theo các quy tắc C++ 11 cho lambdas, vì vậy sự trở lại loại là int. Để trả lại tham chiếu bạn cần sử dụng:

decltype(auto) f(std::vector<int>& v, int i) { return v[i]; } 
+1

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1048 –

+0

@ T.C., Yup, nhưng điều đó không thực sự thay đổi kết quả ở đây, phải không? Tôi đã tham chiếu C++ 11 5.1.2 cố ý, không chỉ 5.1.2, bởi vì câu hỏi được gắn thẻ C++ 11. Tôi nhận thấy DR được coi là một sửa chữa chống lại C++ 11, nhưng vì nó không thực sự thay đổi kết quả ở đây tôi đã đi với các từ ngữ trong C + + 11 như được xuất bản. –

+0

Vâng, không quan trọng trong trường hợp này. –

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