Chyba nie, ale chciałbym potwierdzić. Czy jest pożytek const Foo&&
, gdzie Foo
jest typ klasy?
c++
c++11
constants
rvalue-reference
fredoverflow
źródło
źródło
const&&
są bardzo ważne, chociaż nie mówi dlaczego: youtube.com/watch?v=JhgWFYfdIho#t=54m20sOdpowiedzi:
Czasami są przydatne. Sam szkic C ++ 0x używa ich w kilku miejscach, na przykład:
template <class T> void ref(const T&&) = delete; template <class T> void cref(const T&&) = delete;
Powyższe dwa przeciążeniu zapewnia, że druga
ref(T&)
icref(const T&)
funkcje nie wiążą się rvalues (które w innym przypadku byłyby możliwe).Aktualizacja
Właśnie sprawdziłem oficjalny standard N3290 , który niestety nie jest publicznie dostępny i ma w 20.8 Function objects [function.objects] / p2:
template <class T> void ref(const T&&) = delete; template <class T> void cref(const T&&) = delete;
Następnie sprawdziłem najnowszą wersję roboczą po C ++ 11, która jest publicznie dostępna, N3485 , aw 20.8 Function objects [function.objects] / p2 nadal mówi:
template <class T> void ref(const T&&) = delete; template <class T> void cref(const T&&) = delete;
źródło
const T&&
są używane?const T&&
zapobiega głupiemu używaniu jawnych argumentów szablonu formularzaref<const A&>(...)
. To nie jest strasznie mocny argument, ale kosztconst T&&
przekroczeniaT&&
jest dość minimalny.Semantyka uzyskiwania pliku odniesienia do wartości stałej r (a nie dla
=delete
) polega na stwierdzeniu:Następujący przypadek użycia mógł być IMHO dobrym przypadkiem użycia odniesienia rvalue do const , chociaż język zdecydował się nie stosować tego podejścia (patrz oryginalny post SO ).
Przypadek: inteligentny konstruktor wskaźników z surowego wskaźnika
Zwykle byłoby wskazane użycie
make_unique
imake_shared
, ale obaunique_ptr
ishared_ptr
mogą być skonstruowane z surowego wskaźnika. Oba konstruktory pobierają wskaźnik według wartości i kopiują go. Oba pozwalają (tj. W sensie: nie uniemożliwiają ) kontynuacji używania oryginalnego wskaźnika przekazanego im w konstruktorze.Poniższy kod kompiluje się i daje podwójne wolne :
int* ptr = new int(9); std::unique_ptr<int> p { ptr }; // we forgot that ptr is already being managed delete ptr;
Oba
unique_ptr
ishared_ptr
mogłyby zapobiec powyższemu, gdyby ich odpowiednie konstruktory oczekiwały, że otrzymają surowy wskaźnik jako stałą rwartość , np. Dlaunique_ptr
:unique_ptr(T* const&& p) : ptr{p} {}
W takim przypadku podwójny wolny kod powyżej nie skompilowałby się, ale następujący:
std::unique_ptr<int> p1 { std::move(ptr) }; // more verbose: user moves ownership std::unique_ptr<int> p2 { new int(7) }; // ok, rvalue
Zwróć na to uwagę
ptr
można go nadal używać po przeniesieniu, więc potencjalny błąd nie zniknął całkowicie. Ale jeśli użytkownik jest zobowiązany do wywołaniastd::move
takiego błędu, podlegałoby powszechnej zasadzie: nie używaj zasobu, który został przeniesiony.Można zapytać: OK, ale dlaczego
T*
const&& p
?Powód jest prosty, aby umożliwić tworzenie
unique_ptr
ze wskaźnika stałej . Pamiętaj, że odwołanie do wartości stałej r jest bardziej ogólne niż samo odniesienie do wartości, ponieważ akceptuje zarównoconst
inon-const
. Możemy więc zezwolić na:int* const ptr = new int(9); auto p = std::unique_ptr<int> { std::move(ptr) };
to nie poszłoby, gdybyśmy oczekiwali tylko odniesienia do wartości r (błąd kompilacji: nie można powiązać wartości stałej r z wartością r ).
W każdym razie jest już za późno, aby coś takiego zaproponować. Ale ten pomysł przedstawia rozsądne użycie odniesienia wartości r do const .
źródło
Są dozwolone, a nawet funkcje są uszeregowane w oparciu o
const
, ale ponieważ nie można przejść z obiektu const, do którego się odwołujeconst Foo&&
, nie są one przydatne.źródło
const T&, T&, const T&&, T&&
Oprócz std :: ref , biblioteka standardowa również używa odwołania const rvalue w std :: as_const w tym samym celu.
template <class T> void as_const(const T&&) = delete;
Jest również używany jako wartość zwracana w std :: optional podczas pobierania opakowanej wartości:
constexpr const T&& operator*() const&&; constexpr const T&& value() const &&;
Jak również w 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;
Jest to przypuszczalnie w celu zachowania kategorii wartości, a także stałości opakowania podczas uzyskiwania dostępu do opakowanej wartości.
To robi różnicę, czy funkcja const rvalue ref-qualified można wywołać na opakowanym obiekcie. To powiedziawszy, nie znam żadnych zastosowań funkcji kwalifikowanych const rvalue ref.
źródło
Nie przychodzi mi do głowy sytuacja, w której byłoby to przydatne bezpośrednio, ale można by to wykorzystać pośrednio:
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); }
T w g jest T const, więc f 's x jest T const &&.
Prawdopodobnie skutkuje to błędem kompilacji w f (gdy próbuje przenieść lub użyć obiektu), ale f może przyjąć rvalue-ref, tak że nie można go wywołać na lvalues, bez modyfikowania rvalue (jak w zbyt prostym przykład powyżej).
źródło