Ich denke nicht, aber ich möchte bestätigen. Gibt es eine Verwendung für const Foo&&
, wo Foo
ist ein Klassentyp?
Ich denke nicht, aber ich möchte bestätigen. Gibt es eine Verwendung für const Foo&&
, wo Foo
ist ein Klassentyp?
Antworten:
Sie sind gelegentlich nützlich. Der Entwurf C ++ 0x selbst verwendet sie an einigen Stellen, zum Beispiel:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
Die beiden oben genannten Überladungen stellen sicher, dass die anderen ref(T&)
und cref(const T&)
Funktionen nicht an r-Werte gebunden sind (was sonst möglich wäre).
Aktualisieren
Ich habe gerade den offiziellen Standard N3290 überprüft , der leider nicht öffentlich verfügbar ist und in 20.8 Funktionsobjekten [function.objects] / p2 enthalten ist:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
Dann überprüfte ich den neuesten öffentlich zugänglichen Entwurf nach C ++ 11, N3485 , und in 20.8 Funktionsobjekten [function.objects] / p2 heißt es immer noch:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
const T&&
verwendet?
const T&&
verhindert, dass jemand törichterweise explizite Vorlagenargumente des Formulars verwendet ref<const A&>(...)
. Das ist kein besonders starkes Argument, aber die Kosten für const T&&
Over T&&
sind ziemlich gering.
Die Semantik des Erhaltens einer konstanten Wertreferenz (und nicht für =delete
) besteht darin, zu sagen:
Der folgende Anwendungsfall könnte meiner Meinung nach ein guter Anwendungsfall für den r-Wert-Verweis auf const gewesen sein , obwohl die Sprache beschlossen hat, diesen Ansatz nicht zu wählen (siehe Original-SO-Beitrag ).
Es wäre normalerweise ratsam, make_unique
und zu verwenden make_shared
, aber beides unique_ptr
und shared_ptr
kann aus einem rohen Zeiger konstruiert werden. Beide Konstruktoren erhalten den Zeiger nach Wert und kopieren ihn. Beide erlauben (dh im Sinne von: nicht verhindern ) eine fortgesetzte Verwendung des ursprünglichen Zeigers, der im Konstruktor an sie übergeben wurde.
Der folgende Code wird kompiliert und führt zu double free :
int* ptr = new int(9);
std::unique_ptr<int> p { ptr };
// we forgot that ptr is already being managed
delete ptr;
Sowohl unique_ptr
und shared_ptr
könnte verhindern , dass die oben , wenn ihre entsprechenden Konstrukteure erwarten würde den Rohzeiger zu bekommen als const rvalue , zB für unique_ptr
:
unique_ptr(T* const&& p) : ptr{p} {}
In diesem Fall würde der oben genannte doppelte freie Code nicht kompiliert, aber der folgende würde:
std::unique_ptr<int> p1 { std::move(ptr) }; // more verbose: user moves ownership
std::unique_ptr<int> p2 { new int(7) }; // ok, rvalue
Beachten Sie, dass ptr
dies auch nach dem Verschieben noch verwendet werden kann, sodass der potenzielle Fehler nicht vollständig behoben ist. Wenn der Benutzer jedoch einen std::move
solchen Fehler aufrufen muss, fällt dies unter die allgemeine Regel: Verwenden Sie keine Ressource, die verschoben wurde.
Man kann fragen: OK, aber warum T*
const&& p
?
Der Grund ist einfach, um die Erstellung eines unique_ptr
konstanten Zeigers zu ermöglichen . Denken Sie daran , dass const rvalue Referenz ist allgemeinerer als nur rvalue Referenz , da sie sowohl akzeptiert const
und non-const
. Wir können also Folgendes zulassen:
int* const ptr = new int(9);
auto p = std::unique_ptr<int> { std::move(ptr) };
dies würde nicht gehen , wenn wir nur erwarten rvalue Referenz (Kompilierung - Fehler: kann nicht binden const rvalue zu rvalue ).
Jedenfalls ist dies zu spät, um so etwas vorzuschlagen. Diese Idee bietet jedoch eine vernünftige Verwendung eines r-Wert-Verweises auf const .
Sie sind zulässig und sogar nach Funktionen geordnet. const
Da Sie sich jedoch nicht von dem angegebenen const-Objekt entfernen können const Foo&&
, sind sie nicht nützlich.
const T&, T&, const T&&, T&&
Neben std :: ref verwendet die Standardbibliothek für denselben Zweck auch die Konstantenwertreferenz in std :: as_const .
template <class T>
void as_const(const T&&) = delete;
Es wird auch als Rückgabewert in std :: optional verwendet, wenn der umschlossene Wert abgerufen wird:
constexpr const T&& operator*() const&&;
constexpr const T&& value() const &&;
Sowie in std :: get :
template <class T, class... Types>
constexpr const T&& get(const std::variant<Types...>&& v);
template< class T, class... Types >
constexpr const T&& get(const tuple<Types...>&& t) noexcept;
Dies dient vermutlich dazu, die Wertkategorie sowie die Konstanz des Wrappers beim Zugriff auf den umschlossenen Wert beizubehalten.
Dies macht einen Unterschied, ob const rvalue ref-qualifizierte Funktionen für das umschlossene Objekt aufgerufen werden können. Trotzdem kenne ich keine Verwendungszwecke für qualifizierte Funktionen mit konstantem Wert.
Ich kann mir keine Situation vorstellen, in der dies direkt nützlich wäre, aber es könnte indirekt verwendet werden:
template<class T>
void f(T const &x) {
cout << "lvalue";
}
template<class T>
void f(T &&x) {
cout << "rvalue";
}
template<class T>
void g(T &x) {
f(T());
}
template<class T>
void h(T const &x) {
g(x);
}
Das T in g ist T const, also ist f 's x ein T const &&.
Es ist wahrscheinlich, dass dies zu einem Comile-Fehler in f führt (wenn versucht wird, das Objekt zu verschieben oder zu verwenden), aber f könnte einen rvalue-ref verwenden, so dass es nicht für lvalues aufgerufen werden kann, ohne den rvalue zu ändern (wie im zu einfachen Beispiel oben).
const&&
sehr wichtig sind, obwohl er nicht sagt, warum: youtube.com/watch?v=JhgWFYfdIho#t=54m20s