A T
osztály mozgató konstruktora olyan nem sablon konstruktor, amelynek első paramétere T&&, const T&&, volatile T&& vagy const volatile T&&, és vagy nincs más paraméter, vagy a többi paraméter mind alapértelmezett értékű.
tartalom
- 1 Szintaxis
- 2 Magyarázat
- 3 Implicit módon deklarált mozdulat-konstruktor
- 4 Törölt implicit módon deklarált mozdulat-konstruktor
- 5 Triviális mozdulat-konstruktor
- 6 Eligazítható mozdulat-konstruktor
- 7 Implicit módon-definiált mozgatási konstruktor
- 8 Megjegyzések
- 9 Példa
- 10 Hibajelentések
- 11 Lásd még
Szintaxis
class_name ( class_name && ) |
(1) | (C++11 óta) | |||||||
class_name ( class_name && ) = default; |
(2) | (C++11 óta) | |||||||
class_name ( class_name && ) = delete; |
(3) | (C++11 óta) | |||||||
Ahol a class_name-nek az aktuális osztályt (vagy egy osztálysablon aktuális példányát) kell megneveznie, vagy ha névtérhatáron vagy friend deklarációban deklarálják, akkor minősített osztálynévnek kell lennie.
Magyarázat
A move-konstruktort jellemzően akkor hívjuk meg, amikor egy objektumot inicializálunk (közvetlen inicializálással vagy másolásos inicializálással) rvalue (xvalue vagy prvalue) (C++17-ig)xvalue (C++17 óta) azonos típusból, beleértve a
- inicializálást is: T a = std::move(b); vagy T a(std::move(b));;, ahol
b
típusúT
; - függvény argumentumátadás: f(std::move(a));;, ahol
a
T
típusú ésf
void f(T t); - függvény visszatérése: return a; egy olyan függvényen belül, mint például T f(), ahol
a
T
típusú, amely rendelkezik move konstruktorral.
Ha az inicializáló egy prvalue, a move konstruktor hívása gyakran optimalizálva van (C++17-ig)soha nem történik (C++17 óta), lásd copy elision.
A move konstruktorok jellemzően “ellopják” az argumentum által tartott erőforrásokat (pl.pl. dinamikusan kiosztott objektumokra mutató mutatókat, fájlleírókat, TCP aljzatokat, I/O folyamokat, futó szálakat stb.) ahelyett, hogy másolatokat készítenének róluk, és az argumentumot valamilyen érvényes, de egyébként meghatározhatatlan állapotban hagyják. Például egy std::stringből vagy egy std::vektorból való áthelyezés azt eredményezheti, hogy az argumentum üresen marad. Erre a viselkedésre azonban nem szabad támaszkodni. Néhány típus esetében, mint például az std::unique_ptr, az áthelyezett állapot teljesen meghatározott.
Implicit módon deklarált move-konstruktor
Ha egy osztálytípushoz (struct, class vagy union) nincsenek felhasználó által definiált move-konstruktorok megadva, és az alábbiak mindegyike igaz:
- nincsenek felhasználó által deklarált másolási konstruktorok;
- nincsenek felhasználó által deklarált másolási hozzárendelési operátorok;
- nincsenek felhasználó által deklarált mozgatási hozzárendelési operátorok;
- nincs felhasználó által deklarált destruktor.
akkor a fordító a move konstruktort az osztály nem explicit inline public
tagjaként deklarálja T::T(T&&)
aláírással.
Egy osztálynak több move konstruktora is lehet, például mind a T::T(const T&&), mind a T::T(T&&). Ha van néhány felhasználó által definiált move-konstruktor, a felhasználó a default
kulcsszóval még kikényszerítheti az implicit módon deklarált move-konstruktor létrehozását.
Az implicit módon deklarált (vagy az első deklarációjánál alapértelmezett) mozdulat-konstruktor a dinamikus kivételi specifikációban (C++17-ig)exception specification (C++17 óta)
Törölt implicit módon deklarált mozdulat-konstruktor
A T
osztály implicit módon deklarált vagy alapértelmezett mozdulat-konstruktora töröltnek minősül, ha az alábbiak bármelyike igaz:
-
T
nem statikus adattagokkal rendelkezik, amelyek nem mozgathatók (törölt, elérhetetlen vagy kétértelmű mozgató konstruktorral rendelkeznek); -
T
közvetlen vagy virtuális bázisosztálya van, amely nem mozgatható (törölt, elérhetetlen vagy kétértelmű mozgató konstruktorral rendelkezik); -
T
közvetlen vagy virtuális alaposztálya törölt vagy hozzáférhetetlen destruktorral rendelkezik; -
T
union-szerű osztály, és van egy variáns tagja nem triviális mozgatási konstruktorral.
A törölt alapértelmezett move konstruktort a túlterhelésfeloldás figyelmen kívül hagyja (különben megakadályozná a másolás-inicializálást az rvalue-ból).
Triviális mozgatás-konstruktor
A T
osztály mozgatás-konstruktora triviális, ha az alábbiak közül mindegyik igaz:
- nem felhasználó által biztosított (vagyis implicit módon definiált vagy alapértelmezett);
-
T
nem rendelkezik virtuális tagfüggvényekkel; -
T
nem rendelkezik virtuális bázisosztályokkal; - a
T
minden közvetlen bázisához kiválasztott mozgató konstruktor triviális; - a
T
minden nem statikus osztálytípusú (vagy osztálytípusú tömb) tagjához kiválasztott mozgató konstruktor triviális.
A triviális move konstruktor olyan konstruktor, amely ugyanazt a műveletet hajtja végre, mint a triviális copy konstruktor, azaz másolatot készít az objektum reprezentációjáról, mintha az std::memmove segítségével tenné. Minden C nyelvvel kompatibilis adattípus (POD típusok) triviálisan mozgatható.
Jogosult mozgatható konstruktor
Egy mozgatható konstruktor akkor jogosult, ha nem törölhető. |
(C++20-ig) |
Egy mozgatás-konstruktor akkor alkalmas, ha
|
(C++20 óta) |
A jogosult mozdulat-konstruktorok trivialitása határozza meg, hogy az osztály implicit élettartamú típus-e, és hogy az osztály triviálisan másolható típus-e vagy sem.
Implicit módon definiált mozgáskonstruktor
Ha az implicit módon deklarált mozgáskonstruktor nem törölhető és nem triviális, akkor a fordító definiálja (azaz függvénytestet generál és fordít), ha odr-használat vagy konstanskiértékeléshez szükséges. Union típusok esetén az implicit módon definiált move konstruktor másolja az objektum reprezentációját (mint az std::memmove). A nem uniós osztálytípusok (class és struct) esetében a move konstruktor az objektum bázisainak és nem statikus tagjainak teljes tagonkénti mozgatását végzi el, inicializálási sorrendjükben, közvetlen inicializálással, xvalue argumentummal. Ha ez megfelel a constexpr konstruktorral szemben támasztott követelményeknek, akkor a generált move konstruktor constexpr
.
Megjegyzések
Az erős kivételgarancia lehetővé tétele érdekében a felhasználó által definiált move konstruktorok nem dobhatnak kivételeket. Például az std::vector az std::move_if_noexceptre támaszkodik, hogy válasszon a move és a copy között, amikor az elemeket át kell helyezni.
Ha mind a copy, mind a move konstruktorok meg vannak adva, és más konstruktor nem életképes, a túlterhelés feloldás a move konstruktort választja, ha az argumentum egy azonos típusú r-érték (egy x-érték, például az std::move eredménye vagy egy pr-érték, például egy névtelen ideiglenes (C++17-ig)), és a copy konstruktort választja, ha az argumentum egy l-érték (megnevezett objektum vagy egy l-értékreferenciát visszaadó függvény/operátor). Ha csak a másoló konstruktor van megadva, akkor minden argumentumkategória azt választja ki (amennyiben const-referenciát vesz fel, mivel az rvalue-k const-referenciákhoz is tudnak kötődni), ami a másolást a mozgatás tartalékává teszi, ha a mozgatás nem elérhető.
A konstruktort “mozgatás-konstruktornak” nevezzük, ha paraméterként rvalue-referenciát vesz fel. Nem köteles mozgatni semmit, az osztálynak nem kell, hogy legyen mozgatandó erőforrása, és egy ‘move konstruktor’ nem biztos, hogy képes mozgatni egy erőforrást, mint abban a megengedhető (de talán nem értelmes) esetben, amikor a paraméter egy const rvalue referencia (const T&&).
Példa
#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);}
Kimenet:
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
Hibajelentések
A következő, a viselkedést megváltoztató hibajelentéseket visszamenőleg alkalmazták a korábban közzétett C++ szabványokra.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 1402 | C++11 | egy alapértelmezett move konstruktort töröltek, amely egy nem triviális másoló konstruktort hívna; egy alapértelmezett mozdulat-konstruktor, amelyet töröltek még mindig részt vett a túlterhelés feloldásában |
lehetővé teszi az ilyen másolatkonstruktor hívását; nem vették figyelembe a túlterhelés felbontásban |
CWG 2094 | C++11 | egy alapértelmezett move konstruktorból készült illékony alobjektum nem.triviális (CWG496) |
triviális nem érintett |
Lásd még
- konvertáló konstruktor
- másolás hozzárendelés
- másoló konstruktor.
- másolás elision
- alapértelmezett konstruktor
- destruktor
- explicit
- inicializálás
- aggregált inicializálás
- . konstans inicializálás
- másolás inicializálás
- alapértelmezett inicializálás
- közvetlen inicializálás
- inicializáló lista
- lista inicializálás
- referencia inicializálás
- érték inicializálás
- nulla inicializálás
- mozgatás hozzárendelés
- new