2017-09-06 16 views
10

Bạn có thể giải thích tại sao mã này gặp sự cố không? Tôi mong đợi đầu ra của "a", nhưng tôi nhận được lỗi phân đoạn.Lỗi phân đoạn với thông số std :: function và lambda

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


using namespace std; 

struct MyStruct { 
    vector<string> a; 
    vector<string> b; 
}; 

void out_data(const MyStruct& my_struct, const std::function<const vector<string>&(const MyStruct&)> getter) { 
    cout << getter(my_struct)[0] << endl; 
} 

int main(int argc, char** argv) 
{ 
    MyStruct my_struct; 
    my_struct.a.push_back("a"); 
    my_struct.b.push_back("b"); 
    out_data(my_struct, [](const MyStruct& in) {return in.a;}); 
    return 0; 
} 
+1

Có lẽ vì lambda trả về véc tơ * theo giá trị * và không bằng tham chiếu. –

+4

Nếu bạn thấy mình bao giờ quay trở lại một tham chiếu, hãy tự hỏi "đối tượng đó tôi đang đề cập đến cuộc sống ở đâu"? Nếu bạn không thể trả lời, bạn không nên làm điều đó. – StoryTeller

+1

Vui lòng bật cảnh báo. Trình biên dịch của bạn có lẽ đã hét lên với bạn rằng đây là một ý tưởng tồi. – Yakk

Trả lời

16

Các

[](const MyStruct& in) {return in.a;} 

lambda biểu tương đương với

[](const MyStruct& in) -> auto {return in.a;} 

mà trả về một bản sao của in.a. Ký hiệu std::function của bạn sau đó trả về một tham chiếu treo lơ lửng đối với một đối tượng cục bộ.


Thay đổi biểu thức lambda để

[](const MyStruct& in) -> const auto& {return in.a;} 

để trả về một const& thay vào đó, sửa chữa các segfault.


Ngoài ra, không sử dụng std::function để chuyển lambdas trừ khi bạn có lý do chính đáng để làm như vậy. Tôi khuyên bạn nên đọc bài viết của tôi về chủ đề: "passing functions to functions".

1

Tôi đổ lỗi std::function (và bạn). Tôi đổ lỗi cho bạn, tất nhiên, vì yêu cầu std::function để trả lại một tham chiếu lơ lửng, như được giải thích bởi Vittorio Romeo. Nhưng tôi cũng đổ lỗi cho mẫu xây dựng của std::function vì không kiểm tra trường hợp này, mà phải ở hầu hết hoặc tất cả các trường hợp có thể phát hiện được tại thời gian biên dịch và do đó tạo ra một chẩn đoán. (Tôi sử dụng từ "đổ lỗi" chỉ để chỉ ra các lĩnh vực có thể cải thiện. Trong ý nghĩa đó, tôi cũng đổ lỗi cho bản thân mình vì không nghĩ đến việc thêm kiểm tra chính xác này vào việc thực hiện mẫu lớp học của riêng tôi.

Hãy hãy xem xét kỹ hơn chữ ký của hàm tạo. Tôi đã chọn trang định nghĩa cho mục đích này.

template<typename R, typename... Args> 
template<typename F> 
std::function<R(Args...)>::function(F f); 

Có thể cấm tham chiếu treo ở đây. Một tham chiếu treo lơ lửng sẽ được trả lại từ số operator() nếu và chỉ khi R là tham chiếu đến tạm thời được trả lại bởi F. Hãy xác định (trong phạm vi của cơ thể constructor):

std::is_reference<R>::value && ! std::is_reference<RF>::value 

Có một:

using RF = decltype(f(std::forward<Args>()...)); 

Bây giờ chúng ta có thể gần như chắc chắn rằng một tài liệu tham khảo tòn ten sẽ được trả lại từ 's operator() nếu function tuy nhiên, trong đó, RF có thể là loại lớp với toán tử chuyển đổi do người dùng xác định là R. Mặc dù chuyển đổi này có thể vẫn không an toàn, chúng tôi không có đủ thông tin vào thời điểm này để quyết định và nên sai về mặt tổng quát.Rõ ràng, chúng ta có thể phát hiện xem là mục tiêu của R là một lớp cơ sở công cộng của RF (đây là giả định các điều kiện nêu trên là đúng):

std::is_convertible<RF *, typename std::remove_reference<R>::type *>::value 

tôi chỉ cho phép thừa kế nào ở đây vì std::function chỉ có thể truy cập công cộng không nhập nhằng các lớp cơ sở. Trừ khi ai đó đã thực hiện std::function một friend trong số RF vì một số lý do lạ. (. Kể từ khi chuyển đổi có thể được thực hiện bên trong đối tượng chức năng bao bọc, có lẽ là không cần phải làm điều này)

Đưa nó tất cả cùng nhau và đảo ngược logic, chúng ta có thể thêm tiền tố cơ thể function constructor với:

using RF = decltype(f(std::forward<Args>()...)); 
static_assert(! std::is_reference<R>::value || 
       std::is_reference<RF>::value || 
       ! std::is_convertible<RF *, typename std::remove_reference<R>::type *>::value, 
       "Using this function object would result in a dangling reference in the function call"); 
+0

Mặc dù đây không phải là câu trả lời, tôi đã upvoted là thông tin có giá trị của nó. Bạn đã xem xét nghiên cứu thêm và viết đề xuất/DR? –

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