Move-semantik och Rvalue-referenser

En viktig men komplicerad nyhet i C++11 är ”Move Semantics”. Denna har långtgående konsekvenser och stor betydelse för att undvika onödig kopiering och onödiga temporärer.

En helt ny referenstyp införs, rvalue-referenser, som definieras med två &-tecken så här: T&&. Det är alltså en referens till ett rvalue-uttryck, en temporär. Eftersom temporärer inte sparas kan deras värden flyttas i stället för kopieras. Det är förstås speciellt lönsamt om det är ett komplext objekt där kopieringen tar mycket resurser.

Utrustad med den nya referensen kan vi skapa två nya medlemmar för att kontrollera detta, move-konstruktorn och move-tilldelningsoperatorn.

Class(Class&& c)
Class& operator=(Class&& c)

Om vi använder standardbibliotekets swap funktion kommer den i C++11-versionen att först använda move-konstruktorn för att flytta ett av objekten till ett hjälpobjekt och därefter kommer respektive objekt tilldelas med hjälp av move-tilldelningsoperatorn.

Class c1;
Class c2;
swap(c1, c2);

Ger följande anropssekvens i swap:

Class&& ctor
Class&& operator=
Class&& operator=

Ingen kopiering skedde under bytet. I tidigare versioner av C++ hade objekten kopierats. Hur effektivt det sedan blir beror på implementationen av Class. Hur effektivt ett objekt kan flyttas beror förstås på hur implementationen ser ut. Så helt utan överraskningar är inte detta.

I den gamla kopierings-semantiken lämnades alltså det objekt som man kopierar ifrån i oförändrat och giltigt skick. Med move-semantiken lämnas objektet man flyttar från i ett visserligen giltigt  men oinitierat tillstånd.  Notera skillnaden.

Standardbiblioteket har uppdaterats med detta beteende så bara genom att uppdatera verktygskedjan får man tillgång till detta nya helt automatiskt.

Det här betyder också att en gammal regel att alltid använda const& för icke grundläggande typer i parameterlistor inte längre gäller. Det går utmärkt att använda värdeanrop när det är påkallat. T.ex i följande exempel används både värde anrop samt att värdet returneras från funktionen:

string manipulate(string s)
{
   <... gör något med s ...>
   return s;
}

Tack vara move-semantiken så sker det ingen kopiering ovan.

För gamla C++-hundar som undertecknad kan det nog ta en stund att vänja sig vid detta speciellt som det mesta sker under huven. Om det sker onödiga kopieringar eller inte är ju knappast något som man tittar på i första taget.

När du labbar med detta på egen hand notera då också att returvärdesoptimeringen ibland slår till och undviker kopiering. Detta kan göra att både kopieringar eller flyttningar, som man trodde skulle ske, helt enkelt optimeras bort av kompilatorn, som vet att göra ännu bättre.

 

Publicerad i C/C++

Kategorier

WP to LinkedIn Auto Publish Powered By : XYZScripts.com