2012-04-01 13 views
10

Podczas weekendu próbuję odświeżyć moje umiejętności C++ i nauczyć się C++ 11, natknąłem się na następujący problem: Nie mogę wymusić prawidłowej klasy kontenera konstruktor użycie ruch:Przekazywanie std :: wektor do semantyki konstruktora i ruchu

mam klasy Builder, który jest zdefiniowany w następujący sposób:

class builder 
{ 
    ... 
    container build() const 
    { 
    std::vector<items> items; 

    //... fill up the vector 

    return container(items); //should move the vector right? wrong! 
    //return container(std::move(items)); also doesn't work 
    } 
} 

i klasy element i kontener, zdefiniowane następująco:

class container 
{ 
public: 

    container(std:vector<item> items) 
     : items_(items) // always invokes copy constructor on vector, never move 
    { } 

    container(container&& rhs) 
    { 
     ... 
    } 

    ... 

private: 
    std::vector<item> items_; 

} 

class item 
{ 
public: 
    //move .ctor 
    item(item && rhs); 
    item& operator=(item && rhs); 

    //copy .ctor 
    item(const item& rhs); //this gets called instead of move .ctor 
    item& operator=(const item& rhs); 

    ... 
} 

teraz mój kod s sugerować zastosowanie

builder my_builder; 
... 
auto result = my_builder.build(); 

co powoduje, każdy element, który ma być najpierw skonstruowany i następnie kopiowane ...

Jak mam napisać po classess aby nie kopiować? Czy powinienem wrócić do używania standardowych wskaźników?

+0

Co to jest "item2"? – Mankarse

+0

@Mankarse to był literówka, poprawiony – ghord

Odpowiedz

21

Kod powinien być zmieniony tak:

container(std:vector<item2> items) // may copy OR move 
: items_(std::move(items)) // always moves 
{} 

Ogólnie: jeśli chcesz własną kopię czegoś następnie dokonać tej kopii na tej liście parametrów i przenieść go do miejsca, gdzie musi być. Niech rozmówca będzie tym, który zdecyduje, czy zamierza skopiować lub przenieść istniejące dane. (Innymi słowy byłeś w połowie drogi, teraz po prostu przenieś swoje dane.)

Również: return container(std::move(items));. Nie wspominałem o tym wcześniej, ponieważ błędnie sądziłem, że wszystkie zmienne lokalne zostały automatycznie przeniesione w instrukcji return, ale tylko zwrócona wartość jest. (Tak to powinno rzeczywiście działa: return items;, ponieważ konstruktor container „s nie jest explicit.)

+1

Wyjątkiem od tej ogólnej reguły jest to, że powinieneś przekazać wartość rwartości (a nie wartość), jeśli przekazujesz bardzo duże obiekty, które są drogie w budowie, nawet gdy są przenoszone, lub gdy jesteś pisanie kodu generycznego, który ma być wydajny niezależnie od typów przekazywanych obiektów. – Mankarse

+0

Co powiesz na przypadek, gdy ktoś wywoła ten konstruktor za pomocą wektora, który zostanie użyty później? Czy nie przeniesie to zawartości wektora do tego w pojemniku i usunie je z oryginalnego wektora? – ghord

+0

@Mankarse: Nie widzę tego jako wyjątku od reguły, ponieważ reguła już zaczyna się od "jeśli chcesz własną kopię ...". Jeśli nie potrzebujesz kopii, nie rób jej. Jeśli potrzebujesz jednego, drogiego lub nie, to się wydarzy. – GManNickG

3

napisał ten szablon klasy obsługujące ruch dla Ciebie. Przestudiuj to, a dostaniesz.

/// <summary>Container.</summary> 
class Container { 
private: 
    // Here be data! 
    std::vector<unsigned char> _Bytes; 

public: 
    /// <summary>Default constructor.</summary> 
    Container(){ 
    } 

    /// <summary>Copy constructor.</summary> 
    Container(const Container& Copy){ 
     *this = Copy; 
    } 

    /// <summary>Copy assignment</summary> 
    Container& operator = (const Container& Copy){ 
     // Avoid self assignment 
     if(&Copy == this){ 
      return *this; 
     } 
     // Get copying 
     _Bytes = Copy._Bytes; // Copies _Bytes 
     return *this; 
    } 

    /// <summary>Move constructor</summary> 
    Container(Container&& Move){ 
     // You must do this to pass to move assignment 
     *this = std::move(Move); // <- Important 
    } 

    /// <summary>Move assignment</summary> 
    Container& operator = (Container&& Move){ 
     // Avoid self assignment 
     if(&Move == this){ 
      return *this; 
     } 
     // Get moving 
     std::swap(_Bytes, Move._Bytes); // Moves _Bytes 
     return *this; 
    } 
}; // class Container 

zawsze jestem przeciwko używania argumentów wartości tak:

function(std:vector<item2> items) 

Zawsze używam albo:

function(const std:vector<item2>& items) 
function(std:vector<item2>& items) 
function(std:vector<item2>&& items) 

zwłaszcza dla większych pojemników danych i rzadko:

function(std:vector<item2> items) 

dla mniejszych da ta, nigdy wektory.

W ten sposób masz kontrolę nad tym, co się dzieje i dlatego robimy C++, aby kontrolować wszystko.

  • Jeśli potrzebujesz zapisywalnej kopii, po prostu skopiuj odwołanie do stałej wartości w zmiennej.
  • Jeśli potrzebujesz go tylko do odczytu, odniesienie do const zapobiega nowej kopii.
  • Jeśli chcesz edytować oryginał, skorzystaj z pliku referencyjnego.
  • Używaj argumentów wartości dla małych danych, gdy czujesz się leniwy.

Oczywiście wszystko zależy od tego, co robisz.

Jestem samoukiem programisty C++. Daleko od eksperta, zwłaszcza w slangu C++ ... ale uczenie się :)

+1

[Efektywne przekazywanie argumentów w C++ 11] (http://www.codesynthesis.com/~boris/blog/2012/06/26/efficient-argument-passing-cxx11-part2/) ... dobre wyjaśnienie. – CodeAngry

Powiązane problemy