Deklaruje odniesienie do wartości (dokument propozycji norm).
Oto wstępem do rvalue odniesień .
Oto fantastyczny wygląd pogłębione w rvalue odnośnikach jednej standardowej biblioteki Microsoft programistów .
UWAGA: powiązany artykuł na temat MSDN („Odwołania do wartości: C ++ 0x Funkcje w VC10, część 2”) jest bardzo jasnym wprowadzeniem do odniesień do Rvalue, ale zawiera stwierdzenia o odniesieniach do wartości, które kiedyś były prawdziwe w wersji roboczej C ++ 11 standardowe, ale nie są prawdziwe w przypadku ostatniego! Mówi w szczególności, że w różnych punktach odniesienia do wartości mogą wiązać się z wartościami, które kiedyś były prawdziwe, ale zostały zmienione (np. Int x; int &&rrx = x; już nie kompiluje się w GCC) - drawbarbs 13 lipca 2014 o 16:12
Największą różnicą między referencją C ++ 03 (obecnie nazywaną referencją lvalue w C ++ 11) jest to, że może ona wiązać się z wartością jak tymczasowa bez konieczności bycia const. Zatem ta składnia jest teraz legalna:
T&& r = T();
odniesienia do wartości uwzględniają przede wszystkim:
Przenieś semantykę . Można teraz zdefiniować konstruktor ruchu i operator przypisania ruchu, który pobiera odwołanie do wartości zamiast zwykłego odwołania do wartości stałej. Ruch działa jak kopia, ale nie jest zobowiązany do zachowania źródła bez zmian; w rzeczywistości zwykle modyfikuje źródło tak, że nie jest już właścicielem przeniesionych zasobów. Jest to idealne rozwiązanie do eliminowania obcych kopii, szczególnie w standardowych implementacjach bibliotek.
Na przykład konstruktor kopii może wyglądać następująco:
foo(foo const& other)
{
this->length = other.length;
this->ptr = new int[other.length];
copy(other.ptr, other.ptr + other.length, this->ptr);
}
Jeśli ten konstruktor przejdzie tymczasowo, kopia byłaby niepotrzebna, ponieważ wiemy, że tymczasowy zostanie po prostu zniszczony; dlaczego nie skorzystać z zasobów tymczasowo już przydzielonych? W C ++ 03 nie ma sposobu, aby zapobiec kopiowaniu, ponieważ nie jesteśmy w stanie stwierdzić, że otrzymaliśmy tymczasowe zezwolenie. W C ++ 11 możemy przeciążać konstruktor ruchu:
foo(foo&& other)
{
this->length = other.length;
this->ptr = other.ptr;
other.length = 0;
other.ptr = nullptr;
}
Zauważ tutaj dużą różnicę: konstruktor ruchu faktycznie modyfikuje swój argument. To skutecznie „przeniósłby” tymczasowość do budowanego obiektu, eliminując w ten sposób niepotrzebną kopię.
Konstruktor ruchu byłby używany do tymczasowych i nie stałych const referencji wartości, które są jawnie konwertowane na referencje wartości za pomocą std::move
funkcji (po prostu wykonuje konwersję). Poniższy kod wywołuje konstruktor przenoszenia dla f1
i f2
:
foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty"
Idealne przekazywanie . Odwołania do wartości pozwalają nam poprawnie przekazywać argumenty dla funkcji szablonowych. Weźmy na przykład tę funkcję fabryczną:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
return std::unique_ptr<T>(new T(a1));
}
Jeśli zadzwonimy factory<foo>(5)
, wywnioskowany zostanie argument int&
, który nie będzie wiązał się z literałem 5, nawet jeśli foo
konstruktor przyjmuje argument int
. Cóż, moglibyśmy zamiast tego użyć A1 const&
, ale co jeśli foo
argument konstruktora zostanie przyjęty przez odwołanie inne niż const? Aby prawdziwie rodzajowe funkcji fabryki, mielibyśmy do przeciążenia fabrykę na A1&
i na A1 const&
. Może to być w porządku, jeśli fabryka przyjmuje 1 typ parametru, ale każdy dodatkowy typ parametru pomnożyłby niezbędne ustawienie przeciążenia przez 2. To bardzo szybko nie do utrzymania.
odwołania do wartości rozwiązują ten problem, umożliwiając standardowej bibliotece zdefiniowanie std::forward
funkcji, która może poprawnie przekazywać odniesienia do wartości / wartości. Aby uzyskać więcej informacji o tym std::forward
, jak działa, zobacz tę doskonałą odpowiedź .
To pozwala nam zdefiniować funkcję fabryczną w następujący sposób:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
{
return std::unique_ptr<T>(new T(std::forward<A1>(a1)));
}
Teraz argument rvalue / lvalue-ness argumentu zostaje zachowany po przekazaniu do T
konstruktora. Oznacza to, że jeśli wywoływana jest fabryka z wartością, T
konstruktor jest wywoływany z wartością. Jeśli fabryka jest wywoływana z wartością, T
konstruktor jest wywoływany z wartością. Ulepszona funkcja fabryczna działa dzięki jednej specjalnej zasadzie:
Gdy typ parametru funkcji ma postać, w T&&
której T
jest parametrem szablonu, a argument funkcji jest wartością typu A
, typ A&
służy do odejmowania argumentu szablonu.
W ten sposób możemy użyć fabryki tak:
auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1); // calls foo(foo const&)
Ważne właściwości odniesienia wartości :
- W celu rozwiązania problemu z przeciążeniem wartości lv wolą wiązanie od odwołań do wartości, zaś wartości wolą wiązanie od odwołań do wartości . Dlatego tymczasowe wolą wywoływać konstruktor ruchu / operator przypisania ruchu niż konstruktor kopiowania / operator przypisania.
- Odwołania do wartości będą domyślnie wiązać się z wartościami i tymczasowymi, które są wynikiem niejawnej konwersji . tzn.
float f = 0f; int&& i = f;
jest dobrze uformowany, ponieważ liczba zmiennoprzecinkowa jest domyślnie przekształcalna na int; odniesienie dotyczyłoby tymczasowego wyniku konwersji.
- Nazwane odniesienia wartości są wartościami lv. Nienazwane odniesienia do wartości są wartościami. Ważne jest, aby zrozumieć, dlaczego
std::move
połączenie jest konieczne w:foo&& r = foo(); foo f = std::move(r);