Estoy tratando de proporcionar un contenedor std::invokepara hacer el trabajo de deducir el tipo de función incluso cuando la función está sobrecargada.
(Hice una pregunta relacionada ayer para la versión de puntero variadic y método).
Cuando la función tiene un argumento, este código (C ++ 17) funciona como se espera en condiciones normales de sobrecarga:
#include <functional>
template <typename ReturnType, typename ... Args>
using FunctionType = ReturnType (*)(Args...);
template <typename S, typename T>
auto Invoke (FunctionType<S, T> func, T arg)
{
return std::invoke(func, arg);
}
template <typename S, typename T>
auto Invoke (FunctionType<S, T&> func, T & arg)
{
return std::invoke(func, arg);
}
template <typename S, typename T>
auto Invoke (FunctionType<S, const T&> func, const T & arg)
{
return std::invoke(func, arg);
}
template <typename S, typename T>
auto Invoke (FunctionType<S, T&&> func, T && arg)
{
return std::invoke(func, std::move(arg));
}
Obviamente, es necesario reducir la acumulación de código para obtener más argumentos de entrada, pero ese es un problema separado.
Si el usuario tiene sobrecargas que difieren solo por constantes / referencias, así:
#include <iostream>
void Foo (int &)
{
std::cout << "(int &)" << std::endl;
}
void Foo (const int &)
{
std::cout << "(const int &)" << std::endl;
}
void Foo (int &&)
{
std::cout << "(int &&)" << std::endl;
}
int main()
{
int num;
Foo(num);
Invoke(&Foo, num);
std::cout << std::endl;
Foo(0);
Invoke(&Foo, 0);
}
Luego Invokededuce la función incorrectamente, con salida de g ++:
(int &)
(const int &)(int &&)
(const int &)
Y clang ++:
(int &)
(const int &)(int &&)
(int &&)
(Gracias a geza por señalar que los resultados de clang fueron diferentes).
Entonces Invoketiene un comportamiento indefinido.
Sospecho que la metaprogramación sería la forma de abordar este problema. De todos modos, ¿es posible manejar la deducción de tipo correctamente en el Invokesitio?
(int &&)dos veces para el segundo caso.
Sdeducción de argumentos. Intente comentar la const T &versión de Invokey observe el error. Además, si el argumento se proporciona explícitamente ( Invoke<void>(&Foo, num)), se llama a la versión correcta.
Invoke, puede instanciarla tanto con constante como sin constante Foo. Y no comprueba que el tipo de retorno ( S) sea el mismo para ambos, por lo que dice que no puede deducir S. Entonces ignora esta plantilla. Si bien la creación de instancias de la constante solo Invokese puede hacer con la constante Foo, por lo que se puede deducir Sen este caso. Por lo tanto, el compilador usa esta plantilla.