C++Guns – RoboBlog

22.02.2019

C++ Guns: rvalue referenz, forwarding referenz, universal referenz WTF?

Filed under: Allgemein — Tags: — Thomas @ 18:02

Die gute Nachricht: Normale Menschen müssen das alles nicht verstehen. Auch Anwendungs- Programmierer und library designer brauchen die Details nicht zu wissen. Ihr könnt meine Regeln durchlesen, daran halten und dann STOP.
Die Details sind für Leute, die sich einem 1.5h Vortrag über && anhören können.

Streng genommen wird der Begriff "universal referenz" nicht im C++ std benutzt. Dies ist ein Begriff von Scott Meyers, der ihn benutzt um die Ding begreiflich zu machen. Ich stimme mit ihm überein und benutze in diesem Text nicht den Begriff "forwared referenz" sondern "universal referenz". Viele sagen C++ sei so schon kompliziert genug. Man muss es ja nicht noch schlimmer machen. Weiterhin werde ich Abkürzungen benutzen. rvalue referenz Rref. universal referenz Uref.

Nun, Rref und Uref tauchen (auch) bei ownership transfer auf. Das ist IMO die einzige Stelle bei der man das braucht. Beispiele hierzu in C+ Guns: ACPL ownership transfer.
Nun sehen sich Rref und Uref von der Syntax her ziemlich gleich. Beide haben einen Typ und dann das doppele Kaufmanns-Und, Et-Zeichen &&. Nicht zu verwechseln mit dem logischen AND Operator. Aber für diesen nutzt ihr selbstverständlich and und nicht &&.
Es kommt nun auf den Typ an. Wird er vom Compiler hergeleitet (deduction) und ist es GENAU (im Sinne von EXACT) die Form T&&, dann ist es Uref, sonst Rref. Der Typ T ist ein Template Parameter und sein Name natürlich frei wählbar. Aber sobald ein const hinzugefügt wird, verschwindet das Uref und das Rref taucht auf.

Im ersten Beispiel (in dem verlinkten Artikel) ist somit Rref am Werk. Im zweiten Beispiel Uref. Und im dritten garnichts, weil man von const Variablen nicht moven kann!

Funktionen überladen mit Rref und Uref ist so eine Sache. Uref ist eine _*UNIVERSAL*_!!! Referenz. Sie behandelt ALLES. Und damit ist exakt alles gemeint. Nun ist es technisch (Stand 02.2019 pre C++20) möglich, eine Funktion zu überladen, so dass die eine Rref und die andere Uref akzeptiert. Aber das macht keinen Sinn und ist meiner Meinung nach ein Compilezeit Fehler.

Funktionen mit Rref und "normalen" Referenzen zu überladen ist natürlich möglich, gewollt und hilfreich. Möchte man mit den einen doch etwas anderes machen als mit den anderen (move vs. read only). Und da sich Rref und Uref ähnlich sehen, viel Spass!

Jetzt wird aus jedem Rref und Uref wieder ein ganz normales lvalue. Weil die haben einen Variablennamen und eine Adresse. Zwangsläufig, sonst könnte man den Variablennamen beim programmieren ja nicht eintippen -_-
Also muss wann immer eine Rref oder Uref Variable weiter benutzt wird, ein std::move oder std::forward kommen. Für Rref, also move Operationen nutzen wir std::move. Für Uref, also move oder copy Operationen nutzen wird std::forward. Benutzen wir keines von beiden, ist das meiner Meinung nach ein Compilezeit Fehler. Jemand sollte ein GCC PLugin schreiben.

Regeln:

  • Uref und Rref braucht man (unter anderem) für ownership transfer. Ihr werde es kaum brauchen.
  • Uref genau wenn T ein template parameter und exact die Form T&& vorliegt, ohne const ö.ä, sonst Rref.
  • T&& verhält sich in der Praxis NICHT wie Rref. Es kommt auf T an.
  • Wenn immer T&& im Code auftaucht: Gelber ALARM! Braucht man das wirklich? Wird überall std::move() oder std::forward() angewandt?
  • Von const Variablen kann man nicht moven.
  • Funktionen überladen mit Uref -> Fehler
  • Rref und normale Referenzen überladen -> Gut
  • Rref -> std::move
  • Uref -> std::forward
  • STOP

    Für weitere Informationen zu auto&&, decltype(x), decltype((x)) und sonstige Schmerzen schau euch den Vortrag von Scott Meyers an. Er ist wirklich gut!
    C++ and Beyond 2012: Scott Meyers - Universal References in C++11

    No Comments

    No comments yet.

    RSS feed for comments on this post.

    Sorry, the comment form is closed at this time.

    Powered by WordPress