Estou tentando fornecer um invólucro std::invoke
para deduzir o tipo de função mesmo quando a função está sobrecarregada.
(Fiz uma pergunta relacionada ontem para a versão do ponteiro variável e do método).
Quando a função possui um argumento, esse código (C ++ 17) funciona conforme o esperado em condições normais 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));
}
Reduzir o inchaço do código é obviamente necessário para mais argumentos de entrada, mas esse é um problema separado.
Se o usuário tiver sobrecargas diferentes apenas de const / reference, da seguinte forma:
#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);
}
Em seguida, Invoke
deduz a função incorretamente, com saída g ++:
(int &)
(const & int)(int &&)
(const & int)
E clang ++:
(int &)
(const & int)(int &&)
(int &&)
(Obrigado a Geza por apontar que os resultados do clang eram diferentes).
Então, Invoke
tem um comportamento indefinido.
Suspeito que a metaprogramação seja a maneira de abordar esse problema. Independentemente disso, é possível lidar com a dedução de tipo corretamente no Invoke
site?
(int &&)
duas vezes no segundo caso.
S
dedução de argumento. Tente comentar a const T &
versão Invoke
e observe o erro. Além disso, se o argumento for fornecido explicitamente (Invoke<void>(&Foo, num)
), a versão correta será chamada.
Invoke
, ele pode instancia-lo com o const e o não-const Foo
. E não verifica se o tipo de retorno ( S
) é o mesmo para os dois, portanto diz que não pode deduzir S
. Portanto, ele ignora este modelo. Embora a instanciação da const Invoke
possa ser feita apenas com a const Foo
, é possível deduzirS
nesse caso. Portanto, o compilador usa esse modelo.