Luokan T
move-konstruktori on ei-mallinomainen konstruktori, jonka ensimmäinen parametri on T&&, const T&&, volatile T&& tai const volatile T&&, eikä muita parametreja ole, tai lopuilla parametreilla on kaikilla oletusarvot.
Sisältö
- 1 Syntaksi
- 2 Selitys
- 3 Implisiittisesti ilmoitettu liikkeen konstruktori
- 4 Poistettu implisiittisesti ilmoitettu liikkeen konstruktori
- 5 Triviaalinen liikkeen konstruktori
- 6 Tukikelpoinen liikkeen konstruktori
- 7 Implisiittisesti-defined move constructor
- 8 Huomautuksia
- 9 Esimerkki
- 10 Virheilmoitukset
- 11 Katso myös
Syntaksi
class_name ( class_name && ) |
(1) | (vuodesta C++11) | |||||||
class_name ( class_name && ) = oletus; |
(2) | (vuodesta C++11) | |||||||
luokkanimi ( luokkanimi && ) = delete; |
(3) | (C++11:stä lähtien) | |||||||
Jossa class_name:n on nimettävä nykyinen luokka (tai luokkamallin tämänhetkinen instantiointi), tai kun se on deklaroitu nimiavaruuden vaikutusalueella tai friend-deklaraatiossa, sen on oltava kvalifioitu luokan nimi.
Selitys
Move-konstruktoria kutsutaan tyypillisesti, kun objekti alustetaan (suoralla alustuksella tai kopioinnilla) samantyyppisestä rvalue- (xvalue- tai prvalue-) (C++17:ään asti)xvalue- (C++17:stä lähtien) -arvosta, mukaan lukien
- alustaminen: T a = std::move(b); tai T a(std::move(b));, jossa
b
on tyyppiäT
; - funktioargumenttien välitys: f(std::move(a));, jossa
a
on tyyppiäT
jaf
on void f(T t); - funktion return: return a; sisällä funktiossa, kuten T f(), jossa
a
on tyyppiäT
, jolla on move-konstruktori.
Kun initiaattori on pr-arvo, move-konstruktorin kutsu optimoidaan usein pois (C++17:ään asti)ei koskaan tehdä (C++17:stä lähtien), ks. copy elision.
Move-konstruktorit tyypillisesti ”varastavat” argumentin hallussa olevat resurssit (esim.esim. osoittimet dynaamisesti varattuihin objekteihin, tiedoston kuvaajat, TCP-socketit, I/O-virrat, käynnissä olevat säikeet jne.) sen sijaan, että tekisivät niistä kopioita, ja jättävät argumentin johonkin kelvolliseen, mutta muuten määrittelemättömään tilaan. Esimerkiksi siirtyminen std::stringistä tai std::vectorista voi johtaa siihen, että argumentti jätetään tyhjäksi. Tähän käyttäytymiseen ei kuitenkaan pidä luottaa. Joillekin tyypeille, kuten std::unique_ptr:lle, siirretty tila on täysin määritelty.
Implisiittisesti ilmoitettu move-konstruktori
Jos luokkatyypille (struct, class tai union) ei ole annettu käyttäjän määrittelemiä move-konstruktoreita ja kaikki seuraavat ovat totta:
- ei ole käyttäjän ilmoittamia kopiointikonstruktoreita;
- ei ole käyttäjän ilmoittamia kopioinnin osoitusoperaattoreita;
- ei ole käyttäjän ilmoittamia siirron osoitusoperaattoreita;
- ei ole käyttäjän ilmoittamaa destruktoria.
tällöin kääntäjä ilmoittaa move-konstruktorin luokkansa ei-eksplisiittiseksi inline public
jäseneksi allekirjoituksella T::T(T&&)
.
Luokalla voi olla useita move-konstruktoreita, esimerkiksi sekä T::T(const T&&) että T::T(T&&). Jos joitakin käyttäjän määrittelemiä move-konstruktoreita on olemassa, käyttäjä voi silti pakottaa implisiittisesti ilmoitetun move-konstruktorin tuottamisen avainsanalla default
.
Implisiittisesti ilmoitetulla (tai ensimmäisellä ilmoituksella oletusarvoisesti ilmoitetulla) move-konstruktorilla on poikkeusmäärittely, joka on kuvattu dynaamisessa poikkeusmäärittelyssä (C++17:ään asti)poikkeusmäärittelyssä (C++17:stä lähtien)
Poistettu implisiittisesti ilmoitettu move-konstruktori
Luokan T
implisiittisesti ilmoitettu (tai oletusarvoisesti ilmoitettu) move-konstruktori määritetään poistetuksi, jos mikä tahansa seuraavista on totta:
-
T
:llä on ei-staattisia datajäseniä, joita ei voi siirtää (joilla on poistetut, saavuttamattomat tai epäselvät move-konstruktorit); -
T
:llä on suora tai virtuaalinen perusluokka, jota ei voi siirtää (jolla on poistetut, saavuttamattomat tai epäselvät move-konstruktorit); -
T
on suora tai virtuaalinen perusluokka, jolla on poistettu tai saavuttamattomissa oleva destruktori; -
T
on union-tyyppinen luokka ja sillä on muunnosjäsen, jolla on ei-triviaali siirtokonstruktori.
Ylikuormituksen resoluutio jättää huomiotta oletusarvoisen move-konstruktorin, joka on poistettu (muuten se estäisi kopiointi-initialisoinnin r-arvosta).
Triviaali move-konstruktori
Luokan T
move-konstruktori on triviaali, jos kaikki seuraavista on totta:
- se ei ole käyttäjän tarjoama (eli se on implisiittisesti määritelty tai oletusarvoinen);
-
T
:llä ei ole virtuaalisia jäsenfunktioita; -
T
:llä ei ole virtuaalisia perusluokkia; - jokaiselle
T
:n suoralle kantafunktiolle valitulle siirtokonstruktorille on triviaali; - jokaiselle
T
:n ei-staattiselle luokkatyypin (tai luokkatyypin array) jäsenelle valitulle siirtokonstruktorille on triviaali.
Triviaali move-konstruktori on konstruktori, joka suorittaa saman toiminnon kuin triviaali kopiointikonstruktori, eli tekee kopion objektin esityksestä ikään kuin std::memmovella. Kaikki C-kielen kanssa yhteensopivat tietotyypit (POD-tyypit) ovat triviaalisti siirrettävissä.
Hyväksyttävä siirtokonstruktori
Siirtokonstruktori on hyväksyttävä, jos sitä ei ole poistettu. |
(C++20:een asti) |
Kelpoinen siirtokonstruktori on kelvollinen, jos
|
(C++20:sta lähtien) |
Kelpoisten move-konstruktoreiden triviaalisuus määrittää, onko luokka implisiittisen elinajan tyyppi ja onko luokka triviaalisti kopioitavissa oleva tyyppi.
Implisiittisesti määritelty siirtokonstruktori
Jos implisiittisesti määritelty siirtokonstruktori ei ole poistettu eikä triviaali, kääntäjä määrittelee sen (eli generoi ja kääntää funktion rungon), jos se on odr-käytössä tai sitä tarvitaan vakion evaluointiin. Unionityypeille implisiittisesti määritelty move-konstruktori kopioi objektin esityksen (kuten std::memmove). Muiden kuin union-luokkatyyppien (class ja struct) osalta move-konstruktori suorittaa objektin emästen ja ei-staattisten jäsenten täyden jäsenkohtaisen siirron niiden alustamisjärjestyksessä käyttäen suoraa alustusta xvalue-argumentilla. Jos tämä täyttää constexpr-konstruktorin vaatimukset, generoitu move-konstruktori on constexpr
.
Huomautuksia
Vahvan poikkeustakuun mahdollistamiseksi käyttäjän määrittelemät move-konstruktorit eivät saa heittää poikkeuksia. Esimerkiksi std::vector luottaa std::move_if_noexcept:iin valitessaan siirron ja kopioinnin välillä, kun elementtejä on siirrettävä.
Jos sekä copy- että move-konstruktorit on annettu, eivätkä muut konstruktorit ole käyttökelpoisia, ylikuormituksen resoluutio valitsee move-konstruktorin, jos argumentti on samantyyppinen r-arvo (x-arvo, kuten std::move:n tulos, tai pr-arvo, kuten nimetön väliaikainen (C++17:ään asti)), ja valitsee copy-konstruktorin, jos argumentti on l-arvo (nimetty objekti tai l-arvoviittauksen palauttava funktio/operaattori). Jos vain kopiointikonstruktori on annettu, kaikki argumenttiluokat valitsevat sen (kunhan se ottaa viittauksen constiin, koska rvalueet voivat sitoutua const-viittauksiin), mikä tekee kopioinnista varakeinon siirtämiselle, kun siirtäminen ei ole mahdollista.
Konstruktoria kutsutaan ’siirtämiskonstruktoriksi’, kun se ottaa parametrinaan rvalue-viittauksen. Sen ei ole pakko siirtää mitään, luokalla ei tarvitse olla siirrettävää resurssia ja ’move-konstruktori’ ei välttämättä pysty siirtämään resurssia kuten sallitussa (mutta ehkä ei järkevässä) tapauksessa, jossa parametrina on const rvalue-viittaus (const T&&).
Esimerkki
#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);}
Tulos:
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
Vikailmoitukset
Seuraavia käyttäytymistä muuttavia vikailmoituksia sovellettiin takautuvasti aiemmin julkaistuihin C++ standardeihin.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 1402 | C++11 | poistettiin oletusarvoinen move-konstruktori, joka kutsuisi ei-triviaalia kopiointikonstruktoria; oletusarvoinen move-konstruktori, joka on poistettu osallistui edelleen ylikuormitusratkaisuun |
sallii kutsun tällaiselle kopiokonstruktorille; ei huomioitu ylikuormituksen resoluutiossa |
CWG 2094 | C++11 | haihtuva aliobjekti, joka on tehty oletusarvoisesta liikkeen konstruktorista, joka ei oletriviaali (CWG496) |
triviaalisuus ei vaikuta |
Ks. myös
- konstruktorin muuntaminen
- kopiointikonstruktori
- kopiointitehtävä
- kopiointikonstruktori
- copy elision
- default constructor
- destructor
- explicit
- initialization
- aggregate initialization
- vakioinitialisointi
- kopioinitialisointi
- oletusinitialisointi
- suora inititialisointi
- inititialisointiluettelo
- luettelon inititialisointi
- viittauksen inititialisointi
- arvon inititialisointi
- nollan inititialisointi
- siirto allokointi
- new