2010-04-22 20 views
82

Ich habe einige Probleme, die Notwendigkeit für std::result_of in C++ 0x zu verstehen. Wenn ich richtig verstanden habe, wird result_of verwendet, um den resultierenden Typ des Aufrufs eines Funktionsobjekts mit bestimmten Arten von Parametern zu erhalten. Zum Beispiel:Unterschied zwischen std :: result_of und declltype

template <typename F, typename Arg> 
typename std::result_of<F(Arg)>::type 
invoke(F f, Arg a) 
{ 
    return f(a); 
} 

ich nicht wirklich den Unterschied mit dem folgenden Code sehen:

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 mit diesen beiden Lösungen sehen kann, ist, dass wir müssen entweder:

  • haben eine Instanz des Funktors, um es in dem Ausdruck zu verwenden, der an decltype übergeben wird.
  • kenne einen definierten Konstruktor für den Funktor.

Bin ich recht in der Annahme, dass der einzige Unterschied zwischen decltype und result_of ist, dass der erste einen Ausdruck benötigt, während der zweite nicht der Fall ist?

Antwort

78

result_of war introduced in Boost und dann included in TR1, und schließlich in C++ 0x. Daher hat result_of einen Vorteil, der abwärtskompatibel ist (mit einer geeigneten Bibliothek).

decltype ist eine völlig neue Sache in C++ 0x, beschränkt sich nicht nur auf den Rückgabetyp einer Funktion und ist eine Sprachfunktion.


Wie auch immer, auf gcc 4.5, ist result_of in Bezug auf decltype implementiert:

template<typename _Signature> 
    class result_of; 

    template<typename _Functor, typename... _ArgTypes> 
    struct result_of<_Functor(_ArgTypes...)> 
    { 
     typedef 
     decltype(std::declval<_Functor>()(std::declval<_ArgTypes>()...)) 
     type; 
    }; 
+0

Ok, es ist also hauptsächlich eine Convenience-Klasse, um den hässlichen Ausdruck in decltype zu vermeiden, oder? –

+2

@Luc: Ja. :) (Spät!) – GManNickG

+2

@GMan: Besser spät als nie;)! –

10

Wenn Sie die Art von etwas, das nicht so etwas wie ein Funktionsaufruf, std::result_of einfach nicht sich bewerben. decltype() kann Ihnen den Typ eines beliebigen Ausdrucks geben.

Wenn wir uns auf nur die verschiedenen Möglichkeiten zur Bestimmung des Rückgabetyps eines Funktionsaufrufs beschränken (zwischen std::result_of_t<F(Args...)> und decltype(std::declval<F>()(std::declval<Args>()...)), gibt es einen Unterschied.

std::result_of<F(Args...) ist definiert als:

Wenn der Ausdruck INVOKE (declval<Fn>(), declval<ArgTypes>()...) gut ist wenn sie als unevaluierten Operand (Ziffer 5), das Mitglied typedef Typ nennen die Typ decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...)); anderweitig behandelt gebildet, Es darf kein Mitglied Typ sein.

Der Unterschied zwischen result_of<F(Args..)>::type und decltype(std::declval<F>()(std::declval<Args>()...) ist alles über die INVOKE. Die direkte Verwendung von declval/decltype ist nicht nur ein bisschen länger für die Eingabe, sondern auch nur gültig, wenn F direkt aufrufbar ist (ein Funktionsobjekttyp oder eine Funktion oder ein Funktionszeiger).result_of unterstützt zusätzlich Zeiger auf Memberfunktionen und Zeiger auf Memberdaten.

Anfangs declval/decltype garantiert einen SFINAE-freundlichen Ausdruck, während std::result_of Ihnen einen harten Fehler anstelle eines Ableitungsfehlers geben konnte. Das wurde korrigiert in C++ 14: std::result_of muss nun SFINAE-freundlich sein (Danke an this paper).

Also auf einem konformen C++ 14 Compiler, std::result_of_t<F(Args...)> ist streng überlegen. Es ist klarer, kürzer und korrekt & Dolch; unterstützt mehr F s & Dagger;.


& dolch; Es sei denn, Sie verwenden es in einem Kontext, in dem Sie keine Zeiger auf Elemente zulassen möchten, sodass std::result_of_t in einem Fall erfolgreich sein könnte, in dem es möglicherweise fehlschlägt.

& Dolch; Mit Ausnahmen. Während es Zeiger auf Mitglieder unterstützt, funktioniert result_of nicht, wenn Sie versuchen, eine ungültige Typ-ID zu instanziieren. Diese würden eine Funktion enthalten, die eine Funktion zurückgibt oder abstrakte Typen nach Wert nimmt. Bsp .:

template <class F, class R = result_of_t<F()>> 
R call(F& f) { return f(); } 

int answer() { return 42; } 

call(answer); // nope 

Die korrekte Verwendung wäre result_of_t<F&()> ist, aber das ist ein Detail, das Sie müssen nicht mit decltype erinnern.

+0

Für eine Nicht-Referenz Typ 'T' und eine Funktion' Vorlage Ergebnis_von_t Aufruf (F && f, T && arg) {zurück std :: vorwärts (f) (Std: : move (arg)); } ', ist die Verwendung von' result_of_t' korrekt? –

+0

Wenn das Argument, das wir an 'f' übergeben, ein 'const T' ist, sollten wir dann' result_of_t '? –

Verwandte Themen