2012-05-27 13 views
21

Tengo problemas para detectar por qué diablos no se está compilando. Tengo alguna función lambda que devuelve un std::function según algún argumento.std :: bind a bound function

he reducido mi problema a este fragmento (que no utiliza lambdas, pero reproduce perfectamente mi error):

#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(); 
} 

La llamada a w() produce una de esas salidas de error gcc preciosas en el que No puedo entender qué está pasando mal. Este es el error repetido por 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>)>}] 

Aquí, f debe haber algún objeto invocable que toma un entero como argumento y llama usarlo. Por otro lado, w es simplemente un objeto invocable que llama al some_fun(f), que es f el objeto invocable mencionado anteriormente, que tiene la firma esperada por el parámetro some_fun.

¿Echo de menos algo? Probablemente no sé cómo mezclar realmente std::bind y std::function.

+2

Parece que funciona cuando reemplaza auto con 'std :: function ' para f. – Lalaland

+2

Esto podría no ser una respuesta a su problema ... pero ¿ha considerado usar las funciones lambda nativas que vienen con C++ 11 (que hacen que std :: bind sea innecesario)? – coldfix

+4

En Boost tenemos 'protect' para casos como este, pero no parece haber llegado al estándar. –

Respuesta

21

std::bind expresiones, al igual que sus predecesores boost::bind, admite un tipo de operación de composición. Su expresión para w es aproximadamente equivalente a

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

une la jerarquización de esta manera se interpreta como

  1. Calcular el valor de x.bar<int>(y), donde y es el primer parámetro pasado en el funtor resultante.
  2. Pase ese resultado al some_fun.

Pero x.bar<int>(y) devuelve vacío, no cualquier tipo de función. Es por eso que esto no compila.

Como señala K-ballo, con boost::bind, puede solucionar este problema con boost::protect. Como señalan Kerrek SB y ildjarn, una forma de evitar este problema es: no use auto para f. No desea que f tenga el tipo de expresión de enlace. Si f tiene algún otro tipo, entonces std::bind no intentará aplicar las reglas de composición de funciones.Es posible, por ejemplo, dar f el tipo 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); 

Desde f no literalmente, tener el tipo de una expresión se unen, std::is_bind_expression<>::value habrá falsa del tipo f 's, y por lo tanto la expresión en el std::bind La segunda línea simplemente pasará el valor al pie de la letra, en lugar de intentar aplicar las reglas de composición de la función.

-3

some_fun quiere argumento del tipo const std::function<void(int)> &.

std :: bind devuelve "un objeto de función de tipo no especificado T" (consulte el enlace proporcionado, sección "Valor de retorno"), que está intentando pasar como argumento some_fun.

Parece que esto causa un problema, porque este tipo de argumento no se espera.

Mira: http://en.cppreference.com/w/cpp/utility/functional/bind

+2

Este no es el problema subyacente en absoluto - 'std :: is_bind_expression <>' y su comportamiento es. – ildjarn

Cuestiones relacionadas