2012-05-27 54 views
21

Tôi đang gặp khó khăn trong việc phát hiện tại sao heck này không biên dịch. Tôi đã có một số hàm lambda trả về một số std::function dựa trên một số đối số.std :: ràng buộc một hàm ràng buộc

tôi đã thu hẹp xuống vấn đề của tôi đến đoạn này (mà không sử dụng lambdas, nhưng tái tạo lỗi của tôi một cách hoàn hảo):

#include <functional> 
#include <iostream> 


struct foo { 
    template<class T> 
    void bar(T data) { 
     std::cout << data << "\n"; 
    } 
}; 

void some_fun(const std::function<void(int)> &f) { 
    f(12); 
} 

int main() { 
    foo x; 
    auto f = std::bind(&foo::bar<int>, x, std::placeholders::_1); 
    auto w = std::bind(some_fun, f); 
    w(); 
} 

Các cuộc gọi đến w() sản xuất một trong những kết quả đầu ra lỗi gcc đáng yêu trong đó Tôi không thể hiểu được chuyện gì đang xảy ra. Đây là lỗi lặp lại bởi gcc 4.6.1:

g++ -std=c++0x test.cpp -o test 
test.cpp: In function ‘int main()’: 
test.cpp:20:7: error: no match for call to ‘(std::_Bind<void (*(std::_Bind<std::_Mem_fn<void (foo::*)(int)>(foo, std::_Placeholder<1>)>))(const std::function<void(int)>&)>)()’ 
/usr/include/c++/4.6/functional:1130:11: note: candidates are: 
/usr/include/c++/4.6/functional:1201:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) [with _Args = {_Args ...}, _Result = _Result, _Functor = void (*)(const std::function<void(int)>&), _Bound_args = {std::_Bind<std::_Mem_fn<void (foo::*)(int)>(foo, std::_Placeholder<1>)>}] 
/usr/include/c++/4.6/functional:1215:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const [with _Args = {_Args ...}, _Result = _Result, _Functor = void (*)(const std::function<void(int)>&), _Bound_args = {std::_Bind<std::_Mem_fn<void (foo::*)(int)>(foo, std::_Placeholder<1>)>}] 
/usr/include/c++/4.6/functional:1229:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) volatile [with _Args = {_Args ...}, _Result = _Result, _Functor = void (*)(const std::function<void(int)>&), _Bound_args = {std::_Bind<std::_Mem_fn<void (foo::*)(int)>(foo, std::_Placeholder<1>)>}] 
/usr/include/c++/4.6/functional:1243:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const volatile [with _Args = {_Args ...}, _Result = _Result, _Functor = void (*)(const std::function<void(int)>&), _Bound_args = {std::_Bind<std::_Mem_fn<void (foo::*)(int)>(foo, std::_Placeholder<1>)>}] 

Ở đây, f nên có một số đối tượng có thể được gọi mà phải mất một int như là đối số và gọi sử dụng nó. Mặt khác, w chỉ là một đối tượng có thể gọi được gọi là some_fun(f), là f đối tượng có thể gọi được đề cập ở trên, có ký hiệu được mong đợi bởi tham số some_fun.

Tôi có thiếu gì đó không? Tôi có thể không biết cách trộnvà std::function.

+2

Dường như nó hoạt động khi bạn thay thế tự động bằng 'std :: function ' cho f. – Lalaland

+2

Đây có thể không phải là câu trả lời cho vấn đề của bạn .. nhưng bạn đã cân nhắc việc sử dụng hàm lambda gốc đi kèm với C++ 11 (làm cho std :: bind không cần thiết)? – coldfix

+4

Trong Tăng cường, chúng tôi có 'bảo vệ' cho các trường hợp như thế này, nhưng nó dường như không làm cho nó đạt tiêu chuẩn. –

Trả lời

21

std::bind biểu thức, như boost::bind người tiền nhiệm của họ, hỗ trợ một loại hình hoạt động thành phần. biểu hiện của bạn cho w là tương đương với

auto w=std::bind(some_fun, std::bind(&foo::bar<int>, x, std::placeholders::_1)); 

với phím tắt lồng nhau theo cách này được hiểu như là

  1. Tính giá trị của x.bar<int>(y) nơi y là tham số đầu tiên chuyển vào functor kết quả.
  2. Chuyển kết quả đó vào some_fun.

Nhưng x.bar<int>(y) trả về khoảng trống, không phải bất kỳ loại chức năng nào. Đó là lý do tại sao điều này không biên dịch.

Như K-ballo chỉ ra, với boost::bind, bạn có thể khắc phục vấn đề này với boost::protect. Như Kerrek SB và ildjarn chỉ ra, một cách xung quanh vấn đề này là: không sử dụng auto cho f. Bạn không muốn f để có loại biểu thức ràng buộc. Nếu f có một số loại khác, thì std::bind sẽ không cố gắng áp dụng các quy tắc thành phần chức năng.Bạn có thể, ví dụ, cung cấp cho f loại std::function<void(int)>:

std::function<void(int)> f = std::bind(&foo::bar<int>, x, std::placeholders::_1); 
auto w = std::bind(some_fun, f); 

Kể từ f không theo nghĩa đen có kiểu của một biểu thức ràng buộc, std::is_bind_expression<>::value sẽ là sai lầm về loại f 's, và do đó std::bind biểu hiện trong dòng thứ hai sẽ chỉ chuyển giá trị vào nguyên văn, thay vì cố gắng áp dụng các quy tắc thành phần hàm.

-3

some_fun muốn đối số thuộc loại const std::function<void(int)> &.

std :: bind trả về "đối tượng hàm của loại không xác định T" (xem liên kết được cung cấp, phần "Giá trị trả lại"), mà bạn đang cố gắng chuyển đối số some_fun.

Dường như điều này gây ra sự cố, vì loại đối số này không được mong đợi.

Nhìn vào: http://en.cppreference.com/w/cpp/utility/functional/bind

+2

Đây không phải là vấn đề cơ bản ở tất cả - 'std :: is_bind_expression <>' và hành vi của nó là. – ildjarn