2013-08-06 11 views
20

Ho provato alcune funzionalità di C++ 11 da alcuni. Mi sono imbattuto in riferimenti di valore R e mosso costruttori.C++ 11 costruttore di copie di chiamata di riferimento di riferimento anche

ho implementato il mio primo costruttore mossa, eccolo:

#include <iostream> 
#include <vector> 
using namespace std; 

class TestClass{ 

public: 
    TestClass(int s): 
     size(s), arr(new int[s]){ 
    } 
    ~TestClass(){ 
     if (arr) 
      delete arr; 
    } 
    // copy constructor 
    TestClass(const TestClass& other): 
      size(other.size), arr(new int[other.size]){ 
     std::copy(other.arr, other.arr + other.size, arr); 
    } 

    // move constructor 
    TestClass(TestClass&& other){ 
     arr=other.arr; 
     size=other.size; 

     other.arr=nullptr; 
     other.size=0; 
    } 

private: 
    int size; 
    int * arr; 
}; 

int main(){ 
    vector<TestClass> vec; 

    clock_t start=clock(); 
    for(int i=0;i<500000;i++){ 
     vec.push_back(TestClass(1000)); 
    } 
    clock_t stop=clock(); 
    cout<<stop-start<<endl; 

    return 0; 
} 

Il codice funziona bene. Ad ogni modo inserendo un std :: cout nel costruttore di copie ho notato che viene chiamato! E molte volte .. (sposta il costruttore 500000 volte, copia il costruttore 524287 volte).

Quello che mi ha sorpreso di più è che se commento il costruttore di copie dal codice, l'intero programma diventa molto più veloce, e questa volta il costruttore di movimento è chiamato 1024287 volte.

Qualsiasi indizio?

+1

Quale compilatore stai utilizzando? – doctorlove

+0

http://coliru.stacked-crooked.com/view?id=0b61fede4fd9aef84b124760919e8ca8-4c5ca02fa47b506419b4501a6b65fb4f –

+0

Sto usando gcc 4.8.1 –

risposta

30

Metti noexcept sul costruttore di movimento:

TestClass(TestClass&& other) noexcept { 

Elaborazione: stavo per dare questo Pierre, ma purtroppo la fonte cppreference è solo approssimativamente corretta.

in C++ 03

vector<T>::push_back(T) 

ha la "garanzia forte eccezione". Ciò significa che se lo push_back genera un'eccezione, il vettore viene lasciato nello stesso stato precedente alla chiamata a push_back.

Questa garanzia è problematica se il costruttore di mosse genera un'eccezione.

Quando i vector riassegna, sarebbe come a mossa gli elementi dal vecchio tampone al nuovo. Tuttavia, se una di queste mosse lancia un'eccezione (oltre alla prima), allora viene lasciata in uno stato in cui il vecchio buffer è stato modificato e il nuovo buffer non contiene ancora tutto ciò che si suppone. vector non può ripristinare il vecchio buffer al suo stato originale perché avrebbe dovuto spostare gli elementi indietro per fare ciò, anche quegli spostamenti potrebbero non riuscire.

Così una regola è stata stabilita per il C++ 11:

  1. Se T ha un costruttore noexcept mossa, che può essere utilizzato per spostare gli elementi dal vecchio buffer alla nuova.

  2. Altrimenti se T dispone di un costruttore di copia, verrà utilizzato al suo posto.

  3. Altrimenti (se non esiste un costruttore di copia accessibile), dopo tutto verrà utilizzato il costruttore delle mosse, tuttavia in questo caso non viene più fornita la forte garanzia di sicurezza delle eccezioni.

Chiarimento: "costruttore di copia" in regola 2 significa un costruttore di prendere una const T&, non uno di quelli weenie cosiddetto T& costruttori di copia.:-)

+1

puoi approfondire la tua risposta e cosa fa noxcept? – Alon

+0

Sod tutti su Visual Studio :-(oltre a dire "errore C3646: 'noexcept': identificatore ignoto ignoto" – doctorlove

+0

@Alon http://accu.org/index.php/conferenze/accu_conference_2013/accu2013_sessions # move_noexcept_and_push_back_and_how_it_relates_to_each_other – doctorlove

14

Uso noexcept sul vostro movimento costruttore:

TestClass(TestClass&& other) noexcept { ... } 

noexcept senza un'espressione costante come questo equivale a noexcept(true).

Il compilatore può utilizzare queste informazioni per abilitare alcune ottimizzazioni sulle funzioni non gettando così come consentire all'operatore noexcept, che può controllare al momento della compilazione, se una particolare espressione è dichiarato per gettare le eventuali eccezioni.

Ad esempio, contenitori come std :: vector spostano i loro elementi se il costruttore di elementi di spostamento non è eccetto, e altrimenti copia.

Fonte: http://en.cppreference.com/w/cpp/language/noexcept_spec

NB: Questa è una caratteristica C++ 11. Alcuni compilatori potrebbero non averlo ancora implementato ... (es: Visual Studio 2012)

-1

Un'altra domanda. Nel costruttore mossa,

// move constructor 
TestClass(TestClass&& other){ 
    arr=other.arr; 
    size=other.size; 

    other.arr=nullptr; 
    other.size=0; 
} 

non dovrebbe essere

arr = std: spostare (other.arr);

size = std: move (other.size);

perché

il fatto che tutti i valori di nome (come parametri di funzione) valutano sempre come lvalue (anche quelli dichiarati come riferimenti rvalue)

?

0

Il costruttore di copia viene chiamato quando viene utilizzata tutta la memoria riservata all'interno di std::vector. È necessario chiamare il metodo std::vector::reserve() prima di aggiungere gli elementi.

vector<TestClass> vec; 
vec.reserve(500000);