2015-01-03 20 views
10

Hãy xem xét ví dụ sauChức năng quá tải sử dụng lambda chức năng chữ ký

void foo(const std::function<int()>& f) { 
    std::cout << f() << std::endl; 
} 

void foo(const std::function<int(int x)>& f) { 
std::cout << f(5) << std::endl; 
} 

int main() { 
    foo([](){return 3;}); 

    foo([](int x){return x;}); 
} 

này không biên dịch, vì các cuộc gọi đến foo được cho là mơ hồ. Theo như tôi hiểu điều này là do thực tế, rằng hàm lambda không phải là một ưu tiên là std::function nhưng phải được đúc cho nó và rằng có một hàm tạo std::function lấy một đối số tùy ý.

Có thể ai đó có thể giải thích cho tôi lý do tại sao mọi người sẽ tạo một hàm tạo ngầm ẩn mất một đối số tùy ý. Tuy nhiên câu hỏi acutual của tôi là liệu có một workaround, cho phép sử dụng chữ ký chức năng của hàm lambda để quá tải một chức năng foo. Tôi đã thử các con trỏ hàm, nhưng điều đó không có tác dụng vì việc capture các hàm lambda không thể được truyền tới một con trỏ hàm bình thường.

Mọi trợ giúp đều được chào đón nhiều nhất.

+7

Đây có thể là lỗi trong MSVC. Nó hoạt động trong Clang/GCC. –

+2

Oh Microsoft, bạn rất hài hước –

+0

[Làm việc cho tôi] (http://ideone.com/XmAgLG). Bạn đang sử dụng trình biên dịch nào? –

Trả lời

15

Trình biên dịch của bạn đúng theo C++ 11. Trong C++ 14, một quy tắc được thêm vào nói rằng khuôn mẫu của hàm tạo không được tham gia vào độ phân giải quá tải trừ khi kiểu đối số thực sự có thể gọi với các kiểu đối số của std::function. Do đó, mã này được cho là biên dịch trong C++ 14, nhưng không phải trong C++ 11. Xem xét điều này là một sự giám sát trong C++ 11.

Đối với bây giờ, bạn có thể làm việc này bằng cách chuyển đổi rõ ràng:

foo(std::function<int()>([](){return 3;})); 
+0

Cảm ơn bạn đã làm rõ. Theo các ý kiến ​​tôi đã thử nó bằng cách sử dụng GCC 4.8 (và '-std = C++ 11') và nó đã làm việc. Bằng cách nào đó buồn cười rằng phiên bản mới hơn của GCC do đó không sử dụng đúng C++ 11. – Haatschii

+1

@Haatschii Đó là lỗi trong tiêu chuẩn C++ 11; trình biên dịch/thư viện devs thường áp dụng các bản sửa lỗi hồi tố.(Bên cạnh đó, nó đã được UB trong C++ 11 anyway, vì vậy có hành vi C++ 14 là cho phép.) –

4

http://coliru.stacked-crooked.com/a/26bd4c7e9b88bbd0

Một thay thế cho việc sử dụng std :: chức năng là sử dụng các mẫu. Các mẫu tránh chi phí phân bổ bộ nhớ được kết hợp với std :: function. Máy khấu trừ kiểu mẫu sẽ suy ra loại lambda chính xác được truyền, do đó, việc bỏ trang cuộc gọi sẽ biến mất. Tuy nhiên, bạn vẫn có sự phân biệt quá tải cho trường hợp không có args vs args.

Bạn có thể thực hiện việc này bằng cách sử dụng mẹo có các loại trả về theo sau hoạt động tương tự như enable_if.

template<typename Callable> 
auto baz(Callable c) 
    -> decltype(c(5), void()) 
{ 
    std::cout << c(5) << std::endl; 
} 

Các tình trạng quá tải trên các baz sẽ chỉ có một ứng cử viên quá tải có giá trị khi các mẫu tham số Callable có thể được gọi với một đối số của 5.

Bạn có thể đặt cơ chế tiên tiến hơn trên đầu trang này để làm cho nó tổng quát hơn (ví dụ: mở rộng gói variadic của args thành có thể gọi được) nhưng tôi muốn hiển thị cơ chế cơ bản hoạt động.

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