En flyttkonstruktör av klass T
är en icke-mallkonstruktör vars första parameter är T&&, const T&&, volatile T&& eller const volatile T&&, och antingen finns det inga andra parametrar, eller så har resten av parametrarna alla standardvärden.
Innehåll
- 1 Syntax
- 2 Förklaring
- 3 Implicit deklarerad flyttkonstruktör
- 4 Slopad implicit deklarerad flyttkonstruktör
- 5 Trivial flyttkonstruktör
- 6 Eligibel flyttkonstruktör
- 7 Implicit-definierad flyttkonstruktör
- 8 Anmärkningar
- 9 Exempel
- 10 Felrapporter
- 11 Se även
Syntax
class_name ( class_name && ) |
(1) | (sedan C++11) | |||||||
class_name ( class_name && ) = default; |
(2) | (sedan C++11) | |||||||
class_name ( class_name && ) = delete; |
(3) | (sedan C++11) | |||||||
Varför class_name måste namnge den aktuella klassen (eller den aktuella instantiationen av en klassmall), eller, när den är deklarerad i ett namnområdesomfång eller i en friend-deklaration, måste den vara ett kvalificerat klassnamn.
Förklaring
Förflyttningskonstruktorn anropas vanligtvis när ett objekt initialiseras (genom direktinitialisering eller kopieringsinitialisering) från rvalue (xvalue eller prvalue) (fram till C++17)xvalue (sedan C++17) av samma typ, inklusive
- initialisering: T a = std::move(b); eller T a(std::move(b));;, där
b
är av typenT
; - överlämnande av funktionsargument: f(std::move(a));;, där
a
är av typenT
ochf
är void f(T t); - funktionsåtergång: return a; inuti en funktion som T f(), där
a
är av typenT
som har en move-konstruktör.
När initialisten är ett pr-värde optimeras ofta anropet av flyttkonstruktorn (fram till C++17)aldrig (sedan C++17), se copy elision.
Förflyttningskonstruktörer ”stjäl” vanligtvis resurserna som innehas av argumentet (t.ex.t.ex. pekare på dynamiskt allokerade objekt, filbeskrivare, TCP-socklar, I/O-strömmar, pågående trådar etc.) i stället för att göra kopior av dem, och lämnar argumentet i ett giltigt men på annat sätt obestämt tillstånd. Till exempel kan flyttning från en std::string eller från en std::vector leda till att argumentet lämnas tomt. Man bör dock inte förlita sig på detta beteende. För vissa typer, t.ex. std::unique_ptr, är det flyttade från-tillståndet fullständigt specificerat.
Implicit deklarerad flyttkonstruktör
Om inga användardefinierade flyttkonstruktörer tillhandahålls för en klasstyp (struct, class eller union), och allt av följande är sant:
- det finns inga användardeklarerade kopieringskonstruktörer;
- det finns inga användardeklarerade kopieringstilldelningsoperatörer;
- det finns inga användardeklarerade flytttilldelningsoperatörer;
- det finns ingen användardeklarerad destruktor.
då kommer kompilatorn att deklarera en flyttkonstruktör som en icke-explicit inline public
medlem i sin klass med signaturen T::T(T&&)
.
En klass kan ha flera flyttkonstruktörer, t.ex. både T::T(const T&&) och T::T(T&&). Om det finns några användardefinierade flyttkonstruktörer kan användaren fortfarande tvinga fram genereringen av den implicit deklarerade flyttkonstruktören med nyckelordet default
.
Den implicit deklarerade (eller standardiserade vid sin första deklaration) flyttkonstruktören har en undantagsspecifikation som beskrivs i dynamisk undantagsspecifikation (fram till C++17)undantagsspecifikation (sedan C++17)
Slopad implicit deklarerad flyttkonstruktör
Den implicit deklarerade eller standardiserade flyttkonstruktören för klassen T
definieras som slopad om något av följande är sant:
-
T
har icke-statiska datamedlemmar som inte kan flyttas (har raderade, otillgängliga eller tvetydiga flyttkonstruktorer); -
T
har en direkt eller virtuell basklass som inte kan flyttas (har raderade, otillgängliga eller tvetydiga flyttkonstruktorer); -
T
har en direkt eller virtuell basklass med en raderad eller otillgänglig destruktor; -
T
är en unionsliknande klass och har en variantmedlem med en icke-trivial flyttkonstruktor.
En standardiserad flyttkonstruktör som är raderad ignoreras av överbelastningsupplösning (annars skulle det förhindra kopieringsinitialisering från rvalue).
Trivial flyttkonstruktör
Förflyttningskonstruktören för klass T
är trivial om allt av följande är sant:
- den är inte användarförsedd (vilket innebär att den är implicit definierad eller standardiserad);
-
T
har inga virtuella medlemsfunktioner; -
T
har inga virtuella basklasser; - den flyttkonstruktör som valts för varje direkt bas för
T
är trivial; - den flyttkonstruktör som valts för varje icke-statisk klasstyp (eller array av klasstyp) medlem i
T
är trivial.
En trivial flyttkonstruktör är en konstruktör som utför samma åtgärd som den triviala kopieringskonstruktören, det vill säga gör en kopia av objektrepresentationen som genom std::memmove. Alla datatyper som är kompatibla med C-språket (POD-typer) är trivialt flyttbara.
Godtagbar flyttkonstruktör
En flyttkonstruktör är godtagbar om den inte är raderad. |
(fram till C++20) |
En flyttkonstruktör är valbar om
|
(sedan C++20) |
Trivialiteten hos valbara flyttkonstruktörer avgör om klassen är en implicit livstidstyp och om klassen är en trivialt kopierbar typ.
Implicit definierad flyttkonstruktör
Om den implicit deklarerade flyttkonstruktören varken är raderad eller trivial, definieras den (det vill säga en funktionskropp genereras och kompileras) av kompilatorn om den odr-används eller behövs för konstantutvärdering. För unionstyper kopierar den implicit definierade flyttkonstruktören objektrepresentationen (som med std::memmove). För icke förenade klasstyper (class och struct) utför move-konstruktorn en fullständig medlemsvis flyttning av objektets baser och icke-statiska medlemmar, i deras initialiseringsordning, med hjälp av direkt initialisering med ett xvalue-argument. Om detta uppfyller kraven för en constexpr-konstruktör är den genererade flyttkonstruktören constexpr
.
Noter
För att göra den starka undantagsgarantin möjlig bör användardefinierade flyttkonstruktörer inte kasta undantag. Till exempel förlitar sig std::vector på std::move_if_noexcept för att välja mellan move och copy när elementen måste flyttas.
Om både copy- och move-konstruktörer tillhandahålls och inga andra konstruktörer är genomförbara, väljer överbelastningsupplösning move-konstruktören om argumentet är ett r-värde av samma typ (ett x-värde, t.ex. resultatet av std::move, eller ett pr-värde, t.ex. ett namnlöst temporärt objekt (fram till C++17)) och väljer copy-konstruktören om argumentet är ett l-värde (ett namngivet objekt eller en funktion/operator som returnerar en l-värdereferens). Om endast kopieringskonstruktören tillhandahålls väljer alla argumentkategorier den (så länge den tar en referens till const, eftersom rvalues kan binda till const-referenser), vilket gör kopiering till reservalternativet för flyttning när flyttning inte är tillgänglig.
En konstruktör kallas för en ”move-konstruktör” när den tar en rvalue-referens som parameter. Den är inte skyldig att flytta något, klassen behöver inte ha en resurs som ska flyttas och en ”move-konstruktör” kanske inte kan flytta en resurs som i det tillåtna (men kanske inte förnuftiga) fallet där parametern är en const rvalue-referens (const T&&).
Exempel
#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
Bristrapporter
Följande beteendeförändrande bristrapporter har tillämpats retroaktivt på tidigare publicerade C++-standarder.
DR | Tillämpat på | Beteende som publicerat | Korrekt beteende |
---|---|---|---|
CWG 1402 | C++11 | en förinställd flyttkonstruktör som skulle anropa en icke-trivial kopieringskonstruktör togs bort; En standardiserad flyttkonstruktör som är borttagen har fortfarande deltagit i överbelastningsupplösning |
tillåter anrop av en sådan kopieringskonstruktör; gjorde ignorerad i overload resolution |
CWG 2094 | C++11 | ett flyktigt underobjekt gjort av en standardiserad flyttkonstruktör som inte ärtrivial (CWG496) |
trivialitet inte påverkad |
Se även
- konvertera konstruktör
- kopieringsuppdrag
- kopiera konstruktör
- kopiering av elision
- standardkonstruktör
- destruktör
- explicit
- initialisering
- aggregerad initialisering
- konstant initialisering
- kopieringsinitialisering
- standardinitialisering
- direkt initialisering
- initialiseringslista
- listinitialisering
- referensinitialisering
- referensinitialisering
- värdeinitialisering
- nollinitialisering
- flytta tilldelningen
- ny