Ich versuche, einen Wrapper bereitzustellen, um std::invoke
den Funktionstyp auch dann abzuleiten, wenn die Funktion überlastet ist.
(Ich habe gestern eine verwandte Frage für die Variadic- und Methodenzeigerversion gestellt).
Wenn die Funktion ein Argument hat, funktioniert dieser Code (C ++ 17) unter normalen Überlastbedingungen wie erwartet:
#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));
}
Das Reduzieren des Code-Aufblähens ist offensichtlich für mehr Eingabeargumente erforderlich, aber das ist ein separates Problem.
Wenn der Benutzer Überladungen hat, die sich nur durch const / reference unterscheiden, wie folgt:
#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);
}
Dann Invoke
leitet daraus die Funktion falsch mit g ++ Ausgang:
(int &)
(const int &)(int &&)
(const int &)
Und clang ++:
(int &)
(const int &)(int &&)
(int &&)
(Danke an geza für den Hinweis, dass die Ergebnisse von clang unterschiedlich waren).
Hat Invoke
also undefiniertes Verhalten.
Ich vermute, dass Metaprogrammierung der Weg wäre, um dieses Problem anzugehen. Ist es unabhängig davon möglich, den Typabzug vor Ort korrekt zu handhaben Invoke
?
(int &&)
zweimal für den zweiten Fall gedruckt .
S
Argumentenabzug zu tun haben . Versuchen Sie, die const T &
Version von zu kommentieren Invoke
und den Fehler zu bemerken. Auch wenn das Argument explizit ( Invoke<void>(&Foo, num)
) angegeben wird, wird die richtige Version aufgerufen.
Invoke
, kann er sie sowohl mit der Konstante als auch mit der Nicht-Konstante instanziieren Foo
. Und es wird nicht überprüft, ob der Rückgabetyp ( S
) für beide gleich ist, sodass nicht abgeleitet werden kann S
. Daher wird diese Vorlage ignoriert. Während der Instanziierung Invoke
kann die Konstante nur mit der Konstante ausgeführt werden Foo
, sodass S
in diesem Fall abgeleitet werden kann. Daher verwendet der Compiler diese Vorlage.