2016-01-08 19 views
6

Tôi có đoạn mã sau:Tại sao tôi nhận được lỗi khấu trừ loại cho lambda trả về lambda với nhiều đường dẫn trả về?

int main() { 
    auto f = [] { 
     if (1) return [] { return 1; }; 
     else return [] { return 2; }; 
    }; 
    return f()(); 
} 

điều này làm tăng lỗi biên dịch sau đây sử dụng GCC 5.2.1:

error: inconsistent types ‘main()::<lambda()>::<lambda()>’ and 
     ‘main()::<lambda()>::<lambda()>’ deduced for lambda 
     return type else return [] { return 2; }; 

Bây giờ rõ ràng là hai loại nhìn giống nhau, vì vậy tôi không chắc chắn nếu đây là GCC với một thông báo lỗi gây hiểu lầm hoặc nếu nó thực sự là một lỗi. Theo kiến ​​thức của tôi, điều này sẽ được biên dịch; kiểu trả về lambda nên được suy ra là std::function<int()>. Thật thú vị, nếu tôi khai báo trước lambda return và trả về cùng một biến hai lần, nó hoạt động.

Có ai có thể làm sáng tỏ điều gì đang xảy ra không? Tôi đã tìm thấy các câu hỏi tương tự, nhiều lỗi do lỗi GCC, nhưng điều này có vẻ khác.

+0

họ có các loại khác nhau: https://ideone.com/Eo4OXW –

+0

Cảm ơn để chỉnh sửa câu hỏi của tôi để định dạng mã tốt hơn! Tôi không chắc chắn lý do tại sao tôi nhận được phiếu bầu âm. Tôi rất sẵn lòng đưa thêm "nỗ lực nghiên cứu" nếu cần thiết. Tôi cảm thấy đây là một lỗi biên dịch khá tò mò vì vậy tôi đã hy vọng nhiều người hiểu biết hơn ở đây sẽ biết những gì đang xảy ra. Vui lòng chỉ cho tôi bất kỳ tài nguyên nào tôi nên sử dụng nếu điều này không mang tính cấu trúc. – Alex

+0

Tôi không thực sự hiểu các loại lambda, xin lỗi về điều đó. Các thông báo lỗi không giúp đỡ, tuy nhiên ... – Alex

Trả lời

13

Bây giờ rõ ràng là hai loại là như nhau,

Không, họ không phải vậy. Kiểu của mọi biểu thức lambda là một kiểu riêng biệt, độc đáo.

Từ [expr.prim.lambda]/3:

Các loại lambda-biểu (mà cũng là kiểu của đối tượng đóng) là một độc đáo, chưa đặt tên lớp phi công đoàn loại - được gọi là loại đóng cửa - thuộc tính được mô tả bên dưới.

Do đó, kiểu trả về khấu trừ cho fkhông và không dẫn đến std::function<int()>. Sau này là một loại thư viện không liên quan mà không phải bằng cách nào đó kỳ diệu là "loại phổ biến" của bất kỳ loại đóng cửa nào.

Tất nhiên mỗi loại đóng cửa độc đáo có thể chuyển-std::function<int()>, vì vậy nếu bạn cung cấp các loại trở lại, mọi thứ hoạt động:

auto f = []() -> std::function<int()> { 
    return 1 ? []() { return 1; } 
      : []() { return 2; }; 
}; 

Hoặc, như một chức năng đơn giản:

std::function<int()> f() { 
    return 1 ? []() { return 1; } 
      : []() { return 2; }; 
} 
+0

Tôi đã đề cập đến các chuỗi trong thông điệp trình biên dịch, giống hệt nhau. Tôi cho rằng lý do tại sao tất cả các lambdas đều có loại đơn sắc là một câu hỏi hoàn toàn khác. Tôi đã sử dụng lambdas một thời gian mà không biết điều này và vì vậy nghĩ rằng điều này nên biên dịch. Cảm ơn bạn đã chỉ cho tôi tiêu chuẩn. – Alex

+1

@ Alex: Tôi đồng ý, đó là một thông báo chẩn đoán khủng khiếp. –

+0

@ Alex: Tôi không nghĩ rằng "tại sao lambdas có các loại duy nhất" là một câu hỏi. Đó chỉ là cách họ được định nghĩa. Tôi đoán những gì sẽ là một câu hỏi thực sự là "tại sao chẩn đoán GCC không hiển thị các loại riêng biệt cho các biểu thức lambda khác nhau". Đó cũng là giá trị yêu cầu, có lẽ trên theo dõi lỗi GCC. –

2

Mọi lambda đều có loại duy nhất của riêng nó:

Th biểu thức lambda e xây dựng một đối tượng tạm thời không xác định tên là duy nhất loại không tổng hợp phi đoàn kết chưa được đặt tên [...].

Từ here, nhấn mạnh tôi.

Ngoài ra, lambdas không có gì để làm với std::function, đó là một loại khác.Đặc biệt,

[] { return 1; } 

[] { return 2; } 

có các loại khác nhau. Đây là lý do tại sao khấu trừ không thành công.

1

Các loại đó là không phải là giống nhau. Mỗi lambda là một thể hiện của một kiểu duy nhất, chưa được đặt tên. std::function là mẫu có thể được chuyển đổi hoàn toàn từ các đối tượng có thể gọi thích hợp bao gồm lambdas, nhưng không có sự khởi tạo nào của std::function là loại hoặc loại gốc của bất kỳ lambda nào để không thể suy ra.

Tuy nhiên, bạn có thể nói với trình biên dịch mà bạn muốn nó trở lại std::function và nó sẽ làm việc:

auto f = []() -> std::function<int()> { 
    if (1) return [] { return 1; }; 
    else return [] { return 2; }; 
} 
+1

Chỉ cần chắc chắn, 'std :: function' là một mẫu, không phải là một loại. Không có một loại duy nhất có thể được chuyển đổi từ * bất kỳ * nào có thể gọi. Đối với mỗi loại hàm có một chuyên môn tương ứng của 'std :: function' có thể được chuyển đổi từ các cuộc gọi của một chữ ký tương thích. –

+0

@KerrekSB Nó thoải mái hơn, bạn có thể có 'std :: function' với một chữ ký khác với khả năng gọi miễn là trình biên dịch có thể chuyển đổi các kiểu hoặc điền vào các tham số mặc định, và xử lý các đối tượng lạ cho các phương thức thành viên . Các quy tắc khá phức tạp và tôi đã cố gắng tránh chúng hoàn toàn. – StenSoft

+0

Vâng, đó là lý do tại sao tôi đã cố gắng chen lấn xung quanh bằng cách nói "tương thích". Nhưng nói rộng rãi việc chuyên môn hóa 'std :: function' phải khớp với những gì người gọi có thể cung cấp theo một cách nào đó; không có loại bắt hoàn toàn tùy ý. –

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