2010-11-06 10 views
5

El código siguiente no se compila en gcc 4.5 porque la llamada a foo es ambigua. ¿Cuál es la forma correcta de desambiguarlo?Desambulización de llamadas a funciones que toman std :: functions

#include <iostream> 
#include <functional> 
using namespace std; 

void foo(std::function<void(int, int)> t) 
{ 
    t(1, 2); 
} 

void foo(std::function<void(int)> t) 
{ 
    t(2); 
} 

int main() 
{ 
    foo([](int a, int b){ cout << "a: " << a << " b: " << b << endl;}); 
} 

Respuesta

6

La mejor manera es crear explícitamente un objeto std::function del tipo correcto a continuación, pasar dicho objeto a la función:

std::function<void(int, int)> func = 
    [](int a, int b) { cout << "a: " << a << " b: " << b << endl; } 
foo(func); 

o en línea:

foo(
    std::function<void(int, int)>(
     [](int a, int b) { cout << "a: " << a << "b: " << b << endl; } 
)); 

std::function tiene un constructor plantilla que acepta cualquier cosa:

template<class F> function(F); 

Debido a esto, no hay forma de que el compilador sepa durante la resolución de sobrecarga que foo seleccionar: ambos std::function<void(int)> y std::function<void(int, int)> tienen un constructor que puede tomar su expresión lambda como argumento.

Cuando pasa un objeto std::function directamente, se prefiere el constructor de copia std::function durante la resolución de sobrecarga, por lo que se selecciona en lugar de la plantilla del constructor.


respuesta para el futuro: Si la lista de captura se garantiza que sea vacía, también se puede utilizar punteros a funciones ordinarias. En C++ 0x, un lambda sin captura es implícitamente convertible a un puntero de función. Por lo tanto, se puede usar algo como

void foo(void (*t)(int, int)) { t(1, 2); } 

void foo(void (*t)(int)) { t(1); } 

y llame foo directamente con la lambda captureless (o un puntero de función con el tipo de juego).

Tenga en cuenta que esta conversión es una adición muy reciente al estándar de lenguaje del borrador (se agregó en febrero de este año), por lo que es probable que aún no sea ampliamente compatible. Visual C++ 2010 aún no lo admite; No sé sobre la última g ++.

+0

¿No hay alguna función de fábrica para 'std :: function', ya que se permite la inferencia de tipo en las funciones de plantilla pero no en las clases de plantilla? –

+0

@Ben: No creo que una función de fábrica sea viable en este caso. Considere un objeto de función que tiene dos sobrecargas 'operator()'; ¿Cómo debe seleccionarse el tipo 'std :: function'? –

+0

@James: oops, se olvidó de que incluso un lambda que no captura es un objeto de función. ¿Hay una función de fábrica de los punteros de función o la he perdido por completo? –

3

Recientemente he estado pensando acerca de un problema similar y al mirar alrededor para cualquier soluciones conocidas me encontré con este post y la falta de soluciones para resolver

Una solución alternativa consiste en abstracto sobre el funtor como un argumento de plantilla y use decltype para resolver su tipo. Entonces, el ejemplo anterior sería:

#include <iostream> 
#include <functional> 
using namespace std; 

template<class F> 
auto foo(F t) -> decltype(t(1,2)) 
{ 
    t(1, 2); 
} 

template<class F> 
auto foo(F t) -> decltype(t(2)) 
{ 
    t(2); 
} 

int main() 
{ 
    foo([](int a, int b){ cout << "a: " << a << " b: " << b << endl;}); 
} 

Esto funciona como se esperaba con gcc 4.5.

Cuestiones relacionadas