A move constructor of class T
is a non-template constructor whose first parameter is T&&, const T&&, volatile T&&, or const volatile T&&, and either there are no other parameters, or the rest of the parameters all have default values.
Zawartość
- 1 Składnia
- 2 Objaśnienie
- 3 Konstruktor przeniesienia
- 4 Usunięty konstruktor przeniesienia
- 5 Trywialny konstruktor przeniesienia
- 6 Kwalifikowalny konstruktor przeniesienia
- 7 Konstruktor przeniesienia
- 7 Konstruktor przeniesienia
- .defined move constructor
- 8 Uwagi
- 9 Przykład
- 10 Raporty o wadach
- 11 Zobacz także
Składnia
nazwa_klasy ( nazwa_klasy && ) |
(1) | (od C++11) | |||||||
nazwa_klasy ( nazwa_klasy && ) = domyślna; |
(2) | (od C++11) | |||||||
class_name ( class_name && ) = delete; |
(3) | (od C++11) | |||||||
Gdzie class_name musi zawierać nazwę bieżącej klasy (lub bieżącej instancji szablonu klasy) lub, gdy jest zadeklarowana w przestrzeni nazw lub w deklaracji przyjaciela, musi być kwalifikowaną nazwą klasy.
Objaśnienie
Konstruktor move jest zwykle wywoływany, gdy obiekt jest inicjalizowany (przez bezpośrednią inicjalizację lub kopiowanie) z rvalue (xvalue lub prvalue) (do C++17)xvalue (od C++17) tego samego typu, w tym
- inicjalizacji: T a = std::move(b); lub T a(std::move(b));, gdzie
b
jest typuT
; - przekazywanie argumentów funkcji: f(std::move(a));, gdzie
a
jest typuT
, af
jest void f(T t); - funkcja return: return a; wewnątrz funkcji takiej jak T f(), gdzie
a
jest typuT
, która ma konstruktor move.
Gdy inicjalizatorem jest prvalue, wywołanie konstruktora move jest często optymalizowane out (do C++17)nigdy nie wykonywane (od C++17), patrz copy elision.
Konstruktory move zazwyczaj „kradną” zasoby posiadane przez argument (np.) zamiast tworzyć ich kopie, i pozostawiają argument w jakimś ważnym, ale w inny sposób nieokreślonym stanie. Na przykład, przejście ze std::string lub ze std::vector może spowodować, że argument pozostanie pusty. Jednakże, nie należy polegać na tym zachowaniu. Dla niektórych typów, takich jak std::unique_ptr, stan przeniesienia jest w pełni określony.
Konstruktor przeniesienia deklarowany niejawnie
Jeśli dla typu klasy (struct, class, lub union) nie przewidziano konstruktorów przeniesienia zdefiniowanych przez użytkownika i wszystkie poniższe są prawdziwe:
- nie istnieją zadeklarowane przez użytkownika konstruktory kopiowania;
- nie istnieją zadeklarowane przez użytkownika operatory przypisania kopiowania;
- nie istnieją zadeklarowane przez użytkownika operatory przypisania przenoszenia;
- nie istnieje zadeklarowany przez użytkownika destruktor.
także kompilator zadeklaruje konstruktor move jako jawnego inline public
członka swojej klasy z podpisem T::T(T&&)
.
Klasa może mieć wiele konstruktorów move, np. zarówno T::T(const T&&) jak i T::T(T&&). Jeśli występują konstruktory move zdefiniowane przez użytkownika, użytkownik nadal może wymusić wygenerowanie konstruktora move zadeklarowanego niejawnie za pomocą słowa kluczowego default
.
Niejawnie zadeklarowany (lub domyślny przy pierwszej deklaracji) konstruktor move ma specyfikację wyjątków opisaną w specyfikacji wyjątków dynamicznych (do C++17)specyfikacja wyjątków (od C++17)
Usunięty niejawnie zadeklarowany konstruktor move
Niejawnie zadeklarowany lub domyślny konstruktor move dla klasy T
jest zdefiniowany jako usunięty, jeśli którekolwiek z poniższych jest prawdziwe:
-
T
ma niestatycznych członków danych, którzy nie mogą być przeniesieni (mają usunięte, niedostępne lub niejednoznaczne konstruktory move); -
T
ma bezpośrednią lub wirtualną klasę bazową, która nie może być przeniesiona (ma usunięte, niedostępne lub niejednoznaczne konstruktory move); -
T
ma bezpośrednią lub wirtualną klasę bazową z usuniętym lub niedostępnym destruktorem; -
T
jest klasą podobną do unii i ma członka wariantu z nietrywialnym konstruktorem przeniesienia.
Domyślny konstruktor move, który jest usunięty, jest ignorowany przez rozdzielczość przeciążeniową (w przeciwnym razie uniemożliwiłby kopiowanie-inicjalizację z rvalue).
Trywialny konstruktor move
Konstruktor move dla klasy T
jest trywialny, jeśli wszystkie z poniższych są prawdziwe:
- nie jest on dostarczany przez użytkownika (co oznacza, że jest on domyślnie zdefiniowany lub domyślny);
-
T
nie ma wirtualnych funkcji członkowskich; -
T
nie ma wirtualnych klas bazowych; - konstruktor przeniesienia wybrany dla każdej bezpośredniej bazy
T
jest trywialny; - konstruktor przeniesienia wybrany dla każdego niestatycznego typu klasy (lub tablicy typu klasy) członka
T
jest trywialny.
Trywialny konstruktor move jest konstruktorem, który wykonuje tę samą czynność, co trywialny konstruktor copy, czyli wykonuje kopię reprezentacji obiektu, jak gdyby przez std::memmove. Wszystkie typy danych zgodne z językiem C (typy POD) są trywialnie ruchome.
Kwalifikowalny konstruktor move
Kwalifikowalny jest konstruktor move, jeśli nie jest usunięty. |
(do C++20) |
Kwalifikowany konstruktor przenoszenia jest kwalifikowany, jeśli
|
(od C++20) |
Trywialność kwalifikujących się konstruktorów move określa, czy klasa jest typem implicit-lifetime, i czy klasa jest typem trywialnie kopiowalnym.
Implicitly-defined move constructor
Jeśli implicitly-deklarowany konstruktor move nie jest ani usunięty ani trywialny, jest zdefiniowany (to znaczy, ciało funkcji jest generowane i kompilowane) przez kompilator, jeśli odr-używany lub potrzebny do oceny stałej. Dla typów unii, niejawnie zdefiniowany konstruktor move kopiuje reprezentację obiektu (jak przez std::memmove). Dla typów klasowych nie będących związkami (class i struct), konstruktor move wykonuje pełne przeniesienie baz i niestatycznych członków obiektu, w kolejności ich inicjalizacji, używając bezpośredniej inicjalizacji z argumentem xvalue. Jeśli spełnia to wymagania konstruktora constexpr, wygenerowany konstruktor move jest constexpr
.
Uwagi
Aby umożliwić silną gwarancję wyjątków, konstruktory move zdefiniowane przez użytkownika nie powinny rzucać wyjątków. Na przykład, std::vector polega na std::move_if_noexcept, aby wybrać między move i copy, gdy elementy muszą zostać przeniesione.
Jeśli oba konstruktory copy i move są dostarczone i żaden inny konstruktor nie jest wykonalny, rozdzielczość przeciążenia wybiera konstruktor move jeśli argument jest rvalue tego samego typu (xvalue jak wynik std::move lub prvalue jak bezimienna tymczasowa (do C++17)), i wybiera konstruktor copy jeśli argument jest lvalue (nazwany obiekt lub funkcja/operator zwracająca referencję lvalue). Jeśli podany jest tylko konstruktor kopiowania, wszystkie kategorie argumentów wybierają go (tak długo, jak przyjmuje referencję do const, ponieważ rvalues mogą wiązać się z referencjami const), co czyni kopiowanie awaryjnym dla przenoszenia, gdy przenoszenie jest niedostępne.
Konstruktor jest nazywany „konstruktorem przenoszenia”, gdy przyjmuje referencję rvalue jako parametr. Nie jest on zobowiązany do przenoszenia czegokolwiek, klasa nie musi mieć zasobu, który ma być przeniesiony, a 'konstruktor move’ może nie być w stanie przenieść zasobu, jak w dopuszczalnym (ale może nie rozsądnym) przypadku, gdy parametr jest referencją const rvalue (const T&&).
Przykład
#include <string>#include <iostream>#include <iomanip>#include <utility> struct A{ std::string s; int k; A() : s("test"), k(-1) { } A(const A& o) : s(o.s), k(o.k) { std::cout << "move failed!\n"; } A(A&& o) noexcept : s(std::move(o.s)), // explicit move of a member of class type k(std::exchange(o.k, 0)) // explicit move of a member of non-class type { }}; A f(A a){ return a;} struct B : A{ std::string s2; int n; // implicit move constructor B::(B&&) // calls A's move constructor // calls s2's move constructor // and makes a bitwise copy of n}; struct C : B{ ~C() { } // destructor prevents implicit move constructor C::(C&&)}; struct D : B{ D() { } ~D() { } // destructor would prevent implicit move constructor D::(D&&) D(D&&) = default; // forces a move constructor anyway}; int main(){ std::cout << "Trying to move A\n"; A a1 = f(A()); // return by value move-constructs the target from the function parameter std::cout << "Before move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '\n'; A a2 = std::move(a1); // move-constructs from xvalue std::cout << "After move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '\n'; std::cout << "Trying to move B\n"; B b1; std::cout << "Before move, b1.s = " << std::quoted(b1.s) << "\n"; B b2 = std::move(b1); // calls implicit move constructor std::cout << "After move, b1.s = " << std::quoted(b1.s) << "\n"; std::cout << "Trying to move C\n"; C c1; C c2 = std::move(c1); // calls copy constructor std::cout << "Trying to move D\n"; D d1; D d2 = std::move(d1);}
Output:
Trying to move ABefore move, a1.s = "test" a1.k = -1After move, a1.s = "" a1.k = 0Trying to move BBefore move, b1.s = "test"After move, b1.s = ""Trying to move Cmove failed!Trying to move D
Raporty o defektach
Następujące raporty o defektach zmieniających zachowanie zostały zastosowane retroaktywnie do wcześniej opublikowanych standardów C++.
DR | Zastosowano do | Zachowanie jak opublikowano | Poprawne zachowanie |
---|---|---|---|
CWG 1402 | C++11 | usunięto domyślny konstruktor move, który wywoływałby nietrywialny konstruktor copy; a defaulted move constructor that is deleted still participated in overload resolution |
allows call to such copy constructor; został zignorowany w overload resolution |
CWG 2094 | C++11 | lotny podobiekt wykonany z domyślnego konstruktora move nietrywialny (CWG496) |
trywialność nienaruszona |
Zobacz także
- konstruktor konwertujący
- przypisanie kopiowania
- konstruktor kopiowania
- usuwanie kopii
- konstruktor domyślny
- destruktor
- jawna
- inicjalizacja
- inicjalizacja agregatu
- inicjalizacja stała
- stała inicjalizacja
- kopia inicjalizacja
- inicjalizacja domyślna
- inicjalizacja bezpośrednia
- inicjalizator listy
- inicjalizacja listy
- inicjalizacja referencji
- inicjalizacja wartości
- inicjalizacja zera
- przypisanie przeniesienia
- new