2017-07-16 1 views
3

Hallo ich an einem std::optional Implementierungen hier bei here und ich habe diesen Code-Schnipsel gefunden Suche verwirrt mich:Was macht const_forward bei der optionalen Implementierung in C++?

// workaround: std utility functions aren't constexpr yet 
template <class T> inline constexpr T&& constexpr_forward(typename 
std::remove_reference<T>::type& t) noexcept 
{ 
    return static_cast<T&&>(t); 
} 

diese So verstehe ich nicht auf die folgende Weise:

  1. Was macht typename hier? Nur um den folgenden Teil zu deklarieren, ist ein Typ?
  2. Warum brauchen wir std::remove_reference hier? Haben wir nicht die Referenz zurück in den type& Teil hinzugefügt?
  3. Was bedeutet "std Utility-Funktionen sind noch nicht Constexpr"? Wie macht diese Funktion sie zu Constexpr? Der Körper ist nur ein static_cast.
  4. Diese Funktion wird in einer Reihe von Konstruktoren verwendet und es sieht so aus: template <class... Args> constexpr storage_t(Args&&... args) : value_(constexpr_forward<Args>(args)...) {}, also was macht es hier zu args?

Vielen Dank.

+3

Es produktiver sein kann, zu einer Zeit, eine klare Frage zu stellen (nach SO nach Duplikaten zu suchen.) – juanchopanza

Antwort

5
template <class T> inline constexpr T&& constexpr_forward(typename 
std::remove_reference<T>::type& t) noexcept 
{ 
    return static_cast<T&&>(t); 
} 
  1. Was typename hier macht? Nur um den folgenden Teil zu deklarieren, ist ein Typ?

std::remove_reference<T>::type ist ein abhängiger Typ die T auf den Template-Parametern abhängig ist, also typename wir müssen den Compiler sagen, wir versuchen, einen dependent-name,

  1. Warum brauchen wir std::remove_reference hier? Haben wir nicht die Referenz zurück in den type& Teil hinzugefügt?

Wenn Sie das Beispiel für die Verwendung dieser Nutzenfunktion überprüfen, wie in here

.... 
template <class... Args> explicit constexpr constexpr_optional_base(in_place_t, Args&&... args) 
     : init_(true), storage_(constexpr_forward<Args>(args)...) {} 
... 

Sie können sehen, ein variadische fowarding Referenztyp wird als explizite Template-Argument constexpr_foward<Args>(args)... verwendet. Dies wird die value category des Typs erhalten. Wenn eines der Argumente eine Referenz ist, wird es so aussehen, als hätten wir diese Utility-Funktion mit aufgerufen. Und die instatiation dieser Vorlage wird

inline constexpr Arg01&&& constexpr_forward(Arg01& t) noexcept 
{ 
    return static_cast<Arg01&&&>(t); 
} 

und von reference collapsing rule, haben wir

inline constexpr Arg01& constexpr_forward(Arg01& t) noexcept 
{ 
    return static_cast<Arg01&>(t); 
} 

Eigentlich das Entfernen Referenz sollte es überflüssig sein (lesen Sie über Referenz kollabiert);

  1. Was bedeutet "std Utility-Funktionen noch nicht sind constexpr" bedeuten? Wie macht diese Funktion sie zu Constexpr? Der Körper ist nur ein static_cast.

Es tut nichts anderes, als forwarding eine nicht constexpr Funktion in constexpr Funktionen und Konstruktor.

  1. Diese Funktion ist in einer Reihe von Konstrukteuren verwendet wird, und es sieht wie folgt aus: template <class... Args> constexpr storage_t(Args&&... args) : value_(constexpr_forward<Args>(args)...) {}, so, was es hier nicht tun, um args?

Grundsätzlich wie oben erklärt ..

2

Sie müssen vielleicht einen Schritt zurückgehen und noch etwas mehr lesen, dieser Code ist einigermaßen fortgeschritten und Ihre Fragen deuten darauf hin, dass Sie einige dieser Dinge nicht gelesen haben. Ich würde Modern Effective C++ von Scott Meyers empfehlen. In Bezug auf Ihre speziellen Fragen:

  1. Ja, ist es notwendig, typename zu verwenden, da wir mit einem Mitglied einer Template-Klasse zu tun hat, die ein abhängiger Typ ist. Stellen Sie sich vor, der Compiler könnte sicherstellen, dass es sich um einen Typ handelt, bevor der Compiler es verifizieren kann (weil die Vorlage nicht instanziiert, sondern nur definiert wurde, so dass sie nicht weiß, was T ist).
  2. Im Gegensatz zu den meisten Vorlagenfunktionen besteht die Idee mit darin, den Vorlagenparameter explizit anzugeben, damit er ordnungsgemäß funktioniert. Dieses Formular deaktiviert den Typabzug, was in diesem Zusammenhang wünschenswert ist (selten). Beachten Sie, dass das Entfernen einer Referenz und das Zurücksetzen nicht gleichbedeutend mit dem Nichtstun ist, als ob es sich nicht um eine Referenz handelt, mit der es beginnt. Das ist das Ziel; immer eine lvalue-Referenz nehmen.
  3. Es bedeutet einfach, dass sie nicht constexpr markiert sind. Eine Funktion kann in konstanten Ausdrücken verwendet werden, wenn sie mit constexpr markiert ist. Nicht alle Funktionen können als constexpr markiert werden, es gibt Einschränkungen. Die Standardbibliotheksfunktion std::forward kann conexpr sein, ist aber einfach nicht. Diese Funktion ist nur ein Workaround dafür.
  4. Das ist im Grunde äquivalent zu std::forward, also wäre es gut, darüber zu googeln und darüber zu lesen, da es keine triviale Sache zu grok ist (und das Buch, das ich erwähnte, spricht darüber). Grundsätzlich ist die Idee, dass, sobald Sie eine Funktion haben, die eine Reihe von Argumenten akzeptiert, angenommen wird, dass diese Argumente an anderer Stelle weitergeleitet werden sollen. Die Sache ist, dass alle Symbolnamen (in diesem Fall args) als lvalues ​​betrachtet werden (obwohl sie rvalue-Referenzen sind ... wie gesagt, es ist verwirrend, Typ vs. Wertkategorie). Wenn der Benutzer jedoch irgendwelche Provisorien weitergegeben hat, möchten wir, dass er korrekt (d. H. Als temporär) an die nächste Funktion weitergeleitet wird. std::forward stellt sicher, dass Dinge als rvalues ​​übergeben, in rvalue-Referenzen zurückgeworfen und von der inneren Funktion korrekt verwendet werden können.

Ich hoffe, dass hilft. Auch hier müssen Sie einige Zeit mit dem Lesen verbringen, um ein tieferes Verständnis von Wertkategorien zu erlangen, und rvalue Referenzen, um es vollständig zu verdauen.

+0

Vielen Dank. Ich habe dieses Buch tatsächlich, aber ich hatte keine Zeit es vorher zu lesen. – dorafmon