Estoy tratando de proporcionar un contenedor std::invoke
para 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 Invoke
deduce 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 Invoke
tiene 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 Invoke
sitio?
(int &&)
dos veces para el segundo caso.
S
deducción de argumentos. Intente comentar la const T &
versión de Invoke
y 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 Invoke
se puede hacer con la constante Foo
, por lo que se puede deducir S
en este caso. Por lo tanto, el compilador usa esta plantilla.