c++ - sencillos - std:: function no distingue las funciones sobrecargadas
sobrecarga de operadores en c++ ejemplos sencillos (5)
Otra forma de lidiar con esto es con un lambda genérico en C ++ 14:
int main() {
std::function <void(int, int)> func = [](auto &&... args) { add(std::forward<decltype(args)>(args)...);
}
Eso creará una función lambda que resolverá las cosas sin ambigüedad. No envié argumentos,
Estoy tratando de entender por qué std::function
no puede distinguir entre funciones sobrecargadas.
#include <functional>
void add(int,int){}
class A {};
void add (A, A){}
int main(){
std::function <void(int, int)> func = add;
}
En el código que se muestra arriba, la function<void(int, int)>
puede coincidir solo con una de estas funciones y, sin embargo, falla. ¿Por qué esto es tan? Sé que puedo solucionar esto utilizando un lambda o un puntero de función a la función real y luego almacenando el puntero de función en la función. ¿Pero por qué falla esto? ¿No está claro el contexto sobre qué función quiero ser elegida? Ayúdeme a comprender por qué falla, ya que no puedo entender por qué falla la coincidencia de plantillas en este caso.
Los errores del compilador que obtengo en clang para esto son los siguientes:
test.cpp:10:33: error: no viable conversion from ''<overloaded function type>'' to
''std::function<void (int, int)>''
std::function <void(int, int)> func = add;
^ ~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1266:31: note:
candidate constructor not viable: no overload of ''add'' matching
''std::__1::nullptr_t'' for 1st argument
_LIBCPP_INLINE_VISIBILITY function(nullptr_t) : __f_(0) {}
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1267:5: note:
candidate constructor not viable: no overload of ''add'' matching ''const
std::__1::function<void (int, int)> &'' for 1st argument
function(const function&);
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1269:7: note:
candidate template ignored: couldn''t infer template argument ''_Fp''
function(_Fp,
^
1 error generated.
EDITAR - Además de la respuesta de MSalters, hice algunas búsquedas en este foro y encontré la razón exacta por la que esto falla. Recibí la respuesta de la respuesta de Nawaz en esta post .
Copié una copia de su respuesta aquí:
int test(const std::string&) {
return 0;
}
int test(const std::string*) {
return 0;
}
typedef int (*funtype)(const std::string&);
funtype fun = test; //no cast required now!
std::function<int(const std::string&)> func = fun; //no cast!
Entonces, ¿por qué std::function<int(const std::string&)>
no funciona como funtype fun = test
funciona arriba?
Bueno, la respuesta es, porque std::function
se puede inicializar con cualquier objeto, ya que su constructor está templado, que es independiente del argumento de plantilla que pasó a std::function
.
Para nosotros es obvio qué función tiene la intención de elegir, pero el compilador tiene que seguir las reglas de C ++, no usar saltos de lógica inteligentes (¡o incluso no tan inteligentes, como en casos simples como este!)
El constructor relevante de std::function
es:
template<class F> function(F f);
Que es una plantilla que acepta cualquier tipo.
El estándar C ++ 14 restringe la plantilla (desde LWG DR 2132 ) para que:
no participará en la resolución de sobrecarga a menos que
f
sea invocable (20.9.12.2) para tipos de argumentosArgTypes...
y devuelva tipoR
lo que significa que el compilador solo permitirá llamar al constructor cuando Functor
sea compatible con la firma de llamada de la std::function
(que es void(int, int)
en su ejemplo). En teoría, eso debería significar que el void add(A, A)
no es un argumento viable y, por lo tanto, "obviamente" usted intentó usar el void add(int, int)
.
Sin embargo , el compilador no puede probar la restricción " f
es Callable para los tipos de argumento ..." hasta que conozca el tipo de f
, lo que significa que ya debe haber desambiguado entre void add(int, int)
y void add(A, A)
antes de que pueda aplicar la restricción que le permitiría rechazar una de esas funciones.
Así que hay una situación de gallina y huevo, que desafortunadamente significa que necesita ayudar al compilador a cabo especificando exactamente la sobrecarga de add
que desea usar, y luego el compilador puede aplicar la restricción y (bastante redundantemente) decidir que es aceptable. argumento para el constructor.
Es posible que cambiemos C ++ para que en casos como este todas las funciones sobrecargadas se prueben contra la restricción (por lo que no necesitamos saber cuál probar antes de probarlo) y si solo una es viable, entonces use esa. , pero no es así como funciona C ++.
Por lo que puedo ver, es un problema de Visual Studio.
c ++ 11 estándar (20.8.11)
std::function synopsis
template<class R, class... ArgTypes> class function<R(ArgTypes...)>;
Pero VisualStudio no tiene esa especialización.
clang ++ y g ++ están perfectamente bien con la sobrecarga de std :: funciones
las respuestas anteriores explican por qué VS no funciona, pero no mencionaron que es un error de VS
Si bien es obvio lo que quiere, el problema es que std::function
no puede influir en la resolución de sobrecarga de &add
. Si tuviera que inicializar un puntero de función sin formato ( void (*func)(int,int) = &add
), funciona. Esto se debe a que la inicialización del puntero de función es un contexto en el que se realiza la resolución de sobrecarga. El tipo de objetivo es exactamente conocido. Pero std::function
tomará casi cualquier argumento que sea invocable. Esa flexibilidad en la aceptación de argumentos significa que no se puede hacer una resolución de sobrecarga en &add
. Múltiples sobrecargas de add
pueden ser adecuados.
Un lanzamiento explícito funcionará, es decir, static_cast<void(*)(int, int)> (&add)
.
Esto se puede envolver en una template<typename F> std::function<F> make_function(F*)
que le permitiría escribir auto func = make_function<int(int,int)> (&add)
Tratar:
std::function <void(int, int)> func = static_cast<void(*)(int, int)> (add);
Direcciones para void add(A, A)
y void add(int, int)
obvoiusly differes. Cuando señala la función por su nombre, es bastante imposible para el compilador saber qué dirección de función necesita. void(int, int)
aquí no es una pista.