2013-04-06 20 views
10

Ho un tipo che è possibile copiare, ma può essere costoso da copiare. Ho implementato il costruttore di movimento e l'assegnazione del movimento. Ma ho problemi di prestazioni in cui la gente dimentica di chiamare move() quando passa per valore.Un tipo dovrebbe essere solo in movimento, solo perché la copia può essere costosa?

È buono lo stile C++ 11 per rimuovere il costruttore di copie e fornire invece un metodo di copia esplicita() per i rari casi in cui una copia è effettivamente desiderata? Questo è idiomatico in altri linguaggi (Ruby, JavaScript) ma non conosco nulla nella libreria standard C++ che proibisce la copia puramente per le prestazioni. Ad esempio, std :: vector <> è copiabile, mentre std :: unique_ptr <> e std :: thread non sono copiabili per altri motivi.

+2

Immagino che il problema con una funzione membro 'copia' sia che interrompe tutto il codice modello che utilizza l'operatore di assegnazione. – Pubby

risposta

11

Un tipo deve essere solo in movimento, solo perché la copia può essere costosa?

No. Se i semantica del vostro tipo è tale per cui la copia è concettualmente significativo, quindi il modo corretto per rendere la copia a disposizione è quello di implementare un costruttore di copia, e dare all'utente la possibilità di adottare la sintassi standard per la invoca:

T a; 
T a = b; 

Se la gente si dimentica di passare da oggetti che non vogliono più utilizzare ... Beh, questo è il loro cattivo:

T c = std::move(a); // I'm doing it right (if I no longer need object a); 
T d = b; // If I don't need b anymore, I'm doing it wrong. 

E se (per qualsiasi motivo) per alcune funzioni del vostro che è sempre desiderabile che il chiamante fornisca un oggetto da cui è possibile spostarsi, quindi lasciare che la funzione accetti un Riferimento:

void foo(my_class&& obj); 

my_class a; 
foo(a); // ERROR! 
foo(std::move(a)); // OK 
+1

In alcuni contesti, i tipi di copia non devono essere copiati tramite la costruzione della copia: 'virtuale T * Clone() const = 0' per le classi di interfaccia (o implementazioni non finali) esprime la riproducibilità, ma non può essere implementato come costruttore di copia. Ora, 'value_ptr ' wrapper 'può farti tornare al mondo della costruzione della copia, ma la classe 'T' stessa è copiabile ma non ha un costruttore di copia (almeno non uno pubblico). – Yakk

+1

@Yakk: Non sono d'accordo. Qualsiasi contenitore standard può essere piuttosto costoso da copiare, tuttavia è possibile copiarlo. Personalmente, credo che l'operazione di "copia" abbia una semantica definita dal linguaggio e una sintassi definita dal linguaggio. Queste due cose dovrebbero corrispondere. –

+0

Penso che volevi commentare la mia risposta, non il mio commento. :) – Yakk

1

No. Se il tipo è copiabile, il tipo è copiabile. Ciò significa che il suo costruttore di copie è disponibile e funziona. Non significa che ci sia una funzione membro il cui nome assomiglia ai caratteri c, o, p e in sequenza, che fa "quasi una cosa simile".

+0

Quindi, se disabilitano la costruzione della copia, il tipo non è copiabile. È duplicabile, clonabile o anche altrimenti verbo, ma chiaramente non copiabile. Come è un problema? – Yakk

+1

@Yakk: sono d'accordo con la tua classificazione. È un problema? Bene, chi lo sa? Dipende da cosa vuoi per il tuo tipo. Ma certamente non lo chiamerai più copiabile, e per quanto riguarda molti altri pezzi di codice (ad esempio contenitori), il tuo tipo semplicemente non sarà mai copiato, perché tu l'hai decretato così. –

+0

Giusto, non sono d'accordo con l'analisi. Sto proponendo di rendere il mio tipo non copiabile, nonostante il fatto che sia chiaramente un tipo di valore. –

4

Tratterei la classe come non copiabile nella firma se la copia è sufficientemente costosa. Semanticamente le cose sono riproducibili solo se vuoi che siano, e una copia costosa è un buon motivo per decidere "no, non copiabile".

La possibilità di copiare qualcosa non significa che sia necessario implementarlo in un tipo che è possibile copiare. L'implementatore di quel tipo arriva a decidere se dovrebbe essere semanticamente copiabile.

Non chiamerei l'operazione che ha prodotto una copia "copia" costosa, ma piuttosto "clona" o "duplicata".

Per un modo si potrebbe fare questo:

#include <utility> 

template<typename T> 
struct DoCopy { 
    T const& t; 
    DoCopy(T const& t_):t(t_) {} 
}; 
template<typename T> 
DoCopy<T> do_copy(T const& t) { 
    return t; 
} 
struct Foo { 
    struct ExpensiveToCopy { 
    int _[100000000]; 
    }; 
    ExpensiveToCopy* data; 
    Foo():data(new ExpensiveToCopy()) {} 
    ~Foo(){ delete data; } 
    Foo(Foo&& o):data(o.data) { o.data = nullptr; } 
    Foo& operator=(Foo&& o) { data=o.data; o.data=nullptr; return *this; } 
    Foo& operator=(DoCopy<Foo> o) { 
    delete data; 
    if (o.t.data) { 
     data=new ExpensiveToCopy(*o.t.data); 
    } else { 
     data=new ExpensiveToCopy(); 
    } 
    return *this; 
    } 
    Foo(DoCopy<Foo> cp):data(cp.t.data?new ExpensiveToCopy(*cp.t.data):new ExpensiveToCopy()) {}; 
}; 
int main() { 
    Foo one; 
    // Foo two = one; // illegal 
    Foo three = std::move(one); // legal 
    Foo four; 
    Foo five = do_copy(three); 
    four = std::move(three); 
    five = do_copy(four); 
} 

Questo è in qualche modo simile ai modi si potrebbe avere scritto std::move come la semantica prima della esistenza di riferimenti rvalue, con aspetti negativi simili a tali tecniche, vale a dire che il linguaggio stesso non ha idea di cosa siano gli shenanigans.

ha il vantaggio che la sintassi di cui sopra do_copy è simile alla sintassi di std::move, e consente di utilizzare le espressioni tradizionali senza dover creare istanze di banali Foo poi costruire una copia di un'altra variabile, ecc

Se le situazioni in cui vogliamo trattarlo come copiabili sono comuni (se si vuole evitare), scriverò un copy-wrapper attorno alla classe che conosce il metodo duplicate.

+1

Mi piacerebbe che l'utente della mia biblioteca fosse il giudice. –

+0

Mi piace questa soluzione. Ho fatto qualcosa di simile per le gerarchie copiabili, e la sintassi simile al movimento è un bel tocco. –

Problemi correlati