Ich habe einige Probleme, die Notwendigkeit std::result_of
in C ++ 0x zu verstehen . Wenn ich es richtig verstanden habe, result_of
wird es verwendet, um den resultierenden Typ des Aufrufs eines Funktionsobjekts mit bestimmten Arten von Parametern zu erhalten. Beispielsweise:
template <typename F, typename Arg>
typename std::result_of<F(Arg)>::type
invoke(F f, Arg a)
{
return f(a);
}
Ich sehe den Unterschied mit dem folgenden Code nicht wirklich:
template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(f(a)) //uses the f parameter
{
return f(a);
}
oder
template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(F()(a)); //"constructs" an F
{
return f(a);
}
Das einzige Problem, das ich bei diesen beiden Lösungen sehen kann, ist, dass wir entweder:
- Lassen Sie eine Instanz des Funktors in dem an decltype übergebenen Ausdruck verwenden.
- kennen einen definierten Konstruktor für den Funktor.
Habe ich Recht, wenn ich denke, dass der einzige Unterschied zwischen decltype
und darin result_of
besteht, dass der erste einen Ausdruck braucht, während der zweite dies nicht tut?
decltype
weiß, ist es hässlicher, aber auch mächtiger.result_of
kann nur für aufrufbare Typen verwendet werden und erfordert Typen als Argumente. Zum Beispiel können Sieresult_of
hier nicht verwenden :template <typename T, typename U> auto sum( T t, U u ) -> decltype( t + u );
Wenn die Argumente arithmetische Typen sein können (es gibt keine FunktionF
, die Sie definieren können, umF(T,U)
sie darzustellent+u
. Für benutzerdefinierte Typen könnten Sie. Auf die gleiche Weise (ich habe nicht wirklich damit gespielt) stelle ich mir vor Das Aufrufen von Mitgliedsmethoden könnte schwierig sein,result_of
ohne Bindemittel oder Lambdas zu verwendenstd::declval
dem oben gezeigten Code abgerufen werden . Natürlich ist das hässlich :)result_of
und sein Hilfstypresult_of_t
sind ab C ++ 17 zugunsten voninvoke_result
und veraltetinvoke_result_t
, wodurch einige Einschränkungen des ersteren gemildert werden. Diese sind unten in en.cppreference.com/w/cpp/types/result_of aufgeführt .Wenn Sie den Typ von etwas benötigen, das nicht wie ein Funktionsaufruf ist,
std::result_of
trifft dies einfach nicht zu.decltype()
kann Ihnen den Typ eines beliebigen Ausdrucks geben.Wenn wir uns nur auf die verschiedenen Methoden zur Bestimmung des Rückgabetyps eines Funktionsaufrufs (zwischen
std::result_of_t<F(Args...)>
unddecltype(std::declval<F>()(std::declval<Args>()...)
) beschränken, gibt es einen Unterschied.std::result_of<F(Args...)
ist definiert als:Der Unterschied zwischen
result_of<F(Args..)>::type
unddecltype(std::declval<F>()(std::declval<Args>()...)
ist alles darüberINVOKE
. Die Verwendung vondeclval
/decltype
direkt ist nicht nur etwas länger zu tippen, sondern auch nur gültig, wenn sieF
direkt aufrufbar ist (ein Funktionsobjekttyp oder eine Funktion oder ein Funktionszeiger).result_of
unterstützt zusätzlich Zeiger auf Mitgliederfunktionen und Zeiger auf Mitgliedsdaten.Anfänglich wird ein SFINAE-freundlicher Ausdruck verwendet
declval
/decltype
garantiert, währendstd::result_of
dies zu einem schweren Fehler anstelle eines Abzugsfehlers führen kann.std::result_of
Dies wurde in C ++ 14 korrigiert: Es muss jetzt SFINAE-freundlich sein (dank dieses Dokuments ).Also auf einem konformen C ++ 14-Compiler
std::result_of_t<F(Args...)>
ist es absolut überlegen. Es ist klarer, kürzer und unterstützt † mehrF
s ‡ .† Es sei denn, Sie verwenden es in einem Kontext, in dem Sie keine Zeiger auf Mitglieder zulassen möchten. Dies
std::result_of_t
wäre also in einem Fall erfolgreich, in dem Sie möglicherweise möchten, dass es fehlschlägt.‡ Mit Ausnahmen. Es unterstützt zwar Zeiger auf Mitglieder,
result_of
funktioniert jedoch nicht, wenn Sie versuchen, eine ungültige Typ-ID zu instanziieren . Dazu gehört eine Funktion, die eine Funktion zurückgibt oder abstrakte Typen nach Wert nimmt. Ex.:Die richtige Verwendung wäre gewesen
result_of_t<F&()>
, aber das ist ein Detail, an das Sie sich nicht erinnern müssendecltype
.quelle
T
und eine Funktion korrekt?template<class F> result_of_t<F&&(T&&)> call(F&& f, T&& arg) { return std::forward<F>(f)(std::move(arg)); }
result_of_t
f
istconst T
, sollten wir es auch verwendenresult_of_t<F&&(const T&)>
?