2013-09-28 5 views
5

Ho una classe View e una Shape dove la View "possiede" i suoi oggetti Shape. Lo sto implementando come vettore di unique_ptr. Nella funzione View :: add_shape (std :: unique_ptr & & shape), ho ancora bisogno di usare std :: move sul parametro rvalue per renderlo compilato. Perché? (Usando GCC 4,8)è passato come parametro trattato come lvalue all'interno della funzione?

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

class Shape { }; 
class View 
{ 
    vector<unique_ptr<Shape>> m_shapes; 
    public: 
    void add_shape(unique_ptr<Shape>&& shape) 
    { 
    m_shapes.push_back(std::move(shape));// won't compile without the std::move 
    } 
}; 


int main() 
{ 
    unique_ptr<Shape> ups(new Shape); 
    View v; 
    v.add_shape(std::move(ups)); 
} 

risposta

5

Sì, il riferimento rvalue dal parametro viene utilizzato solo per selezionare la funzione dal punto di vista del chiamante, si comporta come un riferimento Ivalue all'interno della funzione.

Il motivo è che è possibile spostare un valore solo una volta ed è stato ritenuto troppo pericoloso mantenere automaticamente il valore di un parametro. Il tipo di parametro indica che questa funzione accetta un valore rvalue e ne fa potenzialmente uso fornendo un'implementazione che sposta effettivamente il valore. Sovraccaricando questa versione del metodo si può usare a favore della versione non mobile. O semplicemente dice che la funzione richiede un valore.

Ciò che accade nell'implementazione della funzione è un'altra cosa. Si consideri un metodo come

void add_shape_twice(unique_ptr<Shape>&& shape) 
{ 
    m_shapes.push_back(shape); 
    m_shapes.push_back(shape); 
} 

se shape come parametro rimarrebbe un riferimento rvalue: Si avrebbe accidentalmente spostato il valore due volte. Dal momento che nel mondo reale le funzioni potrebbero essere più lunghe ed è piuttosto comune fare riferimento ai parametri più volte (esplicitamente o all'interno di un ciclo o espansione di pacchetto), il potenziale di errori sarebbe enorme.

Anche se saremmo tutti consapevoli di questo e non lo dimenticheremo mai, significherebbe anche che dobbiamo gettare via valore di rvalore e questo renderebbe il codice piuttosto goffo. Aggiungiamo costantemente e rimuoviamo il valore di.

+0

Grazie. Questo risponde alla domanda. Anche se penso che userò il suggerimento di MM per passare dal valore – user1057165

+0

"si comporta come un const -valore-riferimento all'interno della funzione" non dovrebbe comportarsi come un ** non-const ** lvalue in modo che possa essere spostato da? – neuront

+0

@neuront Sì, grazie per averlo fatto notare. Ora è stato risolto, insieme ad alcuni refusi. :) –

3

Sì, dopo l'entrata in funzione che rvalue di riferimento ha un nome ora, quindi è trattare come lvalue. quindi devi move it.

void add_shape(unique_ptr<Shape>&& shape) 
            ^^^^^ 

o passarlo per valore:

void add_shape(unique_ptr<Shape> shape) 
+0

Ho provato il tuo suggerimento di passare per valore, e hai ragione. funziona, purché il chiamante usi ancora std :: move su unique_ptr (poiché il passaggio per valore implica una copia e unique_ptr non ha una copia ctr). Ora tutto ha senso - costringendo il chiamante a usare std :: move sul ptr univoco, è chiaro che la proprietà viene trasferita. – user1057165

+0

come follow-up, questo significa che passare unique_ptr per riferimento è una cattiva idea? Perché ciò non richiede che il chiamante usi esplicitamente std :: move, ma la funzione può ancora spostare il puntatore. – user1057165

+0

No, non è una cattiva idea, ma passare per valore è solo più breve e meno incline agli errori. – deepmax