Domanda

Un problema di "tipi di valore" con risorse esterne (come std::vector<T> o std::string) è che copiandoli tende ad essere molto costoso, e le copie vengono creati implicitamente in vari contesti, quindi questo tende ad essere una preoccupazione di prestazioni. La risposta di C ++ 0x a questo problema è semantica move , che è concettualmente basato sull'idea di furti di risorse e tecnicamente alimentato da riferimenti rvalue .

fa D hanno qualcosa di simile per spostare la semantica o riferimenti rvalue?

È stato utile?

Soluzione

Io credo che ci sono molti posti in D (come ad esempio le strutture di ritorno) che D riesce a rendere li muove mentre C ++ loro una copia farebbe. IIRC, il compilatore farà una mossa piuttosto che una copia in ogni caso in cui si può stabilire che una copia non è necessario, in modo struct copia sta per accadere meno in D che in C ++. E, naturalmente, dal momento che le classi sono riferimenti, non hanno il problema a tutti.

Ma a prescindere, copia costruzione già funziona in modo diverso in D che in C ++. In generale, invece di dichiarare un costruttore di copia, si dichiara un costruttore postblit: this(this). Si fa un memcpy pieno prima this(this) si chiama, e si fa solo tutti i cambiamenti necessari per garantire che la nuova struct è separata da quella originale (ad esempio, facendo una copia completa di variabili membro ove necessario), invece di creare uno completamente nuovo costruttore che deve copiare tutto. Quindi, l'approccio generale è già un po 'diverso dal C ++. E 'inoltre generalmente convenuto che le strutture non dovrebbero avere costosi costruttori postblit - le strutture di copiatura dovrebbe essere a buon mercato - quindi è un problema minore di quanto lo sarebbe in C ++. Oggetti che sarebbero costosi da copiare sono in genere o classi o strutture con semantica di riferimento o di mucca.

I contenitori sono in genere i tipi di riferimento (a Phobos, sono le strutture, piuttosto che le classi, dal momento che non hanno bisogno di polimorfismo, ma la copia di loro non copiare il loro contenuto, in modo che siano ancora tipi di riferimento), in modo da copiare in giro non è costoso come sarebbe in C ++.

Non ci può benissimo essere casi in D in cui potrebbe usare qualcosa di simile a un costruttore mossa, ma in generale, D è stato progettato in modo tale una da ridurre i problemi che C ++ ha con la copia di oggetti intorno, quindi è da nessuna parte nei pressi del problema che è in C ++.

Altri suggerimenti

D hanno un valore e oggetti semantica separate:

  • se si dichiara il tipo di struct, avrà valore semantico di default
  • se si dichiara il tipo di class, avrà oggetto semantico.

Ora, a patto che non gestire la memoria da soli, come è il caso di default in D - utilizzando un garbage collector - bisogna capire che oggetto di tipi dichiarato come class sono automaticamente puntatori (o di "riferimento", se si preferisce ) per l'oggetto reale, non è il vero oggetto stesso.

Così, quando si passa vettori giro in D, quello che passa è il riferimento / puntatore. Automaticamente. Nessuna copia coinvolti (diverso la copia del riferimento).

Questo è il motivo D, C #, Java e altri linguaggi non "necessità" in movimento semantico (come la maggior parte dei tipi sono oggetto semantica e sono manipolati per riferimento, non per la copia).

Forse si potrebbe attuarlo, non sono sicuro. Ma avrebbero realmente ottenere incremento delle prestazioni come in C ++? Per natura, ma non sembrano probabili.

in qualche modo la sensazione che in realtà i riferimenti rvalue e l'intero concetto di "semantica Move" è una conseguenza che è normale in C ++ per creare, oggetti pila "temporanei" locali. In D e la maggior parte delle lingue GC, è più comune per avere oggetti sul mucchio, e allora non c'è in testa di avere un oggetto temporaneo copiato (o spostato) più volte quando si ritorna attraverso una chiamata di stack - così non c'è bisogno di un meccanismo per evitare che la testa anche.

In D (e la maggior parte delle lingue GC) un oggetto class non viene mai copiato in modo implicito e si sta passando solo il riferimento attorno maggior parte del tempo, quindi questa possono media che non è necessario alcun riferimenti rvalue per loro.

OTOH, oggetti struct sono non doveva essere "maniglie alle risorse", ma semplici tipi di valore simile a comportarsi incorporato tipi -. Quindi, di nuovo, non c'è ragione per eventuali semantica di trasferirsi qui, secondo me

Questo produrrebbe una conclusione -. D non ha arbitri rvalue perché non li ha bisogno

riferimenti rvalue Tuttavia, non ho usato, in pratica, ho avuto solo una lettura su di loro, così ho potuto saltato alcuni casi d'uso reali di questa funzione. Si prega di trattare questo post come un mucchio di pensieri al riguardo che si spera sarebbe utile per voi, non come un giudizio affidabile.

Credo che tutte le risposte del tutto non è riuscito a rispondere alla domanda iniziale.

In primo luogo, come si è detto, la questione è rilevante solo per le strutture. Le classi hanno nessuna mossa significativa. Inoltre detto sopra, per le strutture, una certa quantità di movimento avverrà automaticamente dal compilatore in determinate condizioni.

Se si desidera ottenere il controllo sulle operazioni di spostamento, ecco cosa devi fare. È possibile disattivare la copia annotando questo (questa) con @disable. Successivamente, è possibile ignorare constructor(constructor &&that) C ++ s 'definendo this(Struct that). Allo stesso modo, è possibile ignorare l'assegnazione con opAssign(Struct that). In entrambi i casi, è necessario fare in modo che distruggere i valori della that.

Per l'assegnazione, dal momento che è anche necessario per distruggere il vecchio valore della this, il modo più semplice è quello di scambiare loro. Un'implementazione di unique_ptr C ++ s 'sarebbe, quindi, simile a questa:

struct UniquePtr(T) {
    private T* ptr = null;

    @disable this(this); // This disables both copy construction and opAssign

    // The obvious constructor, destructor and accessor
    this(T* ptr) {
        if(ptr !is null)
            this.ptr = ptr;
    }

    ~this() {
        freeMemory(ptr);
    }

    inout(T)* get() inout {
        return ptr;
    }

    // Move operations
    this(UniquePtr!T that) {
        this.ptr = that.ptr;
        that.ptr = null;
    }

    ref UniquePtr!T opAssign(UniquePtr!T that) { // Notice no "ref" on "that"
        swap(this.ptr, that.ptr); // We change it anyways, because it's a temporary
        return this;
    }
}

Modifica: Avviso non mi definisco opAssign(ref UniquePtr!T that). Questo è l'operatore di assegnamento per copia, e se si tenta di definirlo, il compilatore errore fuori perché avete dichiarato, nella linea @disable, che si dispone di alcuna cosa.

Credo che se avete bisogno della fonte di perdere la risorsa si potrebbe essere nei guai. Tuttavia essendo GC'ed si può spesso evitare di doversi preoccupare di più proprietari quindi potrebbe non essere un problema per la maggior parte dei casi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top