2011-01-19 14 views
10

Dado el siguiente código: -Sobrecarga en std :: función <...>

#include <algorithm> 
#include <iostream> 
#include <functional> 
#include <string> 

void func(std::function<void(void)> param) 
{ 
    param(); 
} 

void func(std::function<void(int)> param) 
{ 
    param(5); 
} 

int main(int argc, char* argv[]) 
{ 
    func([]() { std::cout << "void(void)" << std::endl; }); 
    func([] (int i) { std::cout << "void(int): " << i << std::endl; }); 

    std::string line; 
    std::getline(std::cin, line); 
    return 0; 
} 

error de compilación de VS2010: -

CppTest.cpp(18): error C2668: 'func' : ambiguous call to overloaded function 
1>   CppTest.cpp(11): could be 'void func(std::tr1::function<_Fty>)' 
1>   with 
1>   [ 
1>    _Fty=void (int) 
1>   ] 
1>   CppTest.cpp(6): or  'void func(std::tr1::function<_Fty>)' 
1>   with 
1>   [ 
1>    _Fty=void (void) 
1>   ] 
1>   while trying to match the argument list '(`anonymous-namespace'::<lambda0>)' 
1>CppTest.cpp(19): error C2668: 'func' : ambiguous call to overloaded function 
1>   CppTest.cpp(11): could be 'void func(std::tr1::function<_Fty>)' 
1>   with 
1>   [ 
1>    _Fty=void (int) 
1>   ] 
1>   CppTest.cpp(6): or  'void func(std::tr1::function<_Fty>)' 
1>   with 
1>   [ 
1>    _Fty=void (void) 
1>   ] 
1>   while trying to match the argument list '(`anonymous-namespace'::<lambda1>)' 

error de compilación de g ++ - 4,5

program2.cpp: In function ‘int main(int, char**)’: 
program2.cpp:18:68: error: call of overloaded ‘func(main(int, char**)::<lambda()>)’ is ambiguous 
program2.cpp:6:10: note: candidates are: void func(std::function<void()>) 
program2.cpp:11:10: note:     void func(std::function<void(int)>) 
program2.cpp:19:79: error: call of overloaded ‘func(main(int, char**)::<lambda(int)>)’ is ambiguous 
program2.cpp:6:10: note: candidates are: void func(std::function<void()>) 
program2.cpp:11:10: note:     void func(std::function<void(int)>) 

Así parece que el compilador no puede entender que un lambda []() -> void solo se puede asignar a una función std :: < void (void) >, y un lambda [] (int) -> void solo se puede asignar a std :: function < void (int) >. ¿Se supone que esto suceda o solo una deficiencia en los compiladores?

Respuesta

12

¿Se supone que esto suceda o solo una deficiencia en los compiladores?

Se supone que esto debe suceder. std::function tiene una plantilla de constructor que puede tomar un argumento de cualquier tipo. El compilador no puede saber hasta después de que se haya seleccionado una plantilla de constructor e instanciado que se ejecutará en errores, y tiene que poder seleccionar una sobrecarga de su función antes de que pueda hacer eso.

La solución más sencilla es utilizar un yeso o para construir de forma explícita un objeto std::function del tipo correcto:

func(std::function<void()>([](){})); 
func(std::function<void(int)>([](int){})); 

Si usted tiene un compilador que apoya la conversión captureless-lambda-a-la función de puntero y su lambda no captura nada, puede utilizar los punteros de función primas:

void func(void (*param)()) { } 
void func(void (*param)(int)) { } 

(parece que está utilizando Visual C++ 2010, que no soporta esta conversión la conversión no se agregó a la especificación hasta. solo befo re Visual Studio 2010 enviado, demasiado tarde para añadirlo en)


para explicar el problema en un poco más de detalle, considere lo siguiente:.

template <typename T> 
struct function { 

    template <typename U> 
    function(U f) { } 
}; 

Esto es básicamente lo que el std::function constructor en pregunta: Puedes llamarlo con cualquier argumento, incluso si el argumento no tiene sentido y causaría un error en otro lugar. Por ejemplo, function<int()> f(42); invocaría esta plantilla de constructor con U = int.

En el ejemplo específico, el compilador encuentra dos funciones candidatas durante la resolución de sobrecarga:

void func(std::function<void(void)>) 
void func(std::function<void(int)>) 

El tipo de argumento, algunos nombre de tipo lambda inefable que nos referiremos como F, no coincide con ninguno de estos exactamente, por lo que el compilador comienza a ver qué conversiones puede hacer al F para intentar hacer que coincida con una de estas funciones candidatas. Cuando busca conversiones, encuentra la plantilla de constructor antes mencionada.

Todo el compilador ve en este punto es que se puede llamar a cualquiera de las funciones debido

  • puede convertir F a std::function<void(void)> utilizando su constructor conversión con U = F y
  • puede convertir F a std::function<void(int)> mediante su conversión constructor con U = F.

En su ejemplo, es obvio que solo una de ellas tendrá éxito sin error, pero en el caso general eso no es cierto. El compilador no puede hacer nada más. Tiene que informar la ambigüedad y falla. No puede elegir uno porque ambas conversiones son igualmente buenas y ninguna sobrecarga es mejor que la otra.

+0

Dime si tengo este derecho. Este fragmento tiene el mismo problema por la misma razón (¿no se puede instanciar Struct a tiempo para determinar la sobrecarga de func)? 'plantilla struct Struct {Struct (T var) {}}; void func2 (Struct obj) {} void func2 (Struct obj) {} int main() {func2 (2,5); } ' – Arnavion

+0

No, es un poco diferente a eso. Ese error es porque 'T' está en un contexto no deducido. Editaré una explicación en mi respuesta; este comentario no es lo suficientemente grande. –

+0

Entendido. ¡Gracias! – Arnavion

Cuestiones relacionadas