Domanda

È indicato in [C ++ 11: 12.8/31]:

Questa elisione delle operazioni di copia/spostamento, chiamata Copy Elision, è consentita [...]:

-In un'istruzione di ritorno in una funzione con un tipo di restituzione di classe, quando l'espressione è il nome di un oggetto automatico non volatile (diverso da una funzione o un parametro di cattura) Con lo stesso tipo non qualificato CV del tipo di restituzione della funzione, l'operazione di copia/spostamento può essere omessa costruendo l'oggetto automatico direttamente nel valore di restituzione della funzione

Ciò implica

#include <iostream>

using namespace std;

struct X
{
    X() { }
    X(const X& other) { cout << "X(const X& other)" << endl; }
};

X no_rvo(X x) {
    cout << "no_rvo" << endl;
    return x;
}

int main() {
    X x_orig;
    X x_copy = no_rvo(x_orig);

    return 0;
}

Stamparrà

X(const X& other)
no_rvo
X(const X& other)

Perché è richiesto il secondo costruttore di copie? Un compilatore non può semplicemente prolungare la vita di X?

È stato utile?

Soluzione

Immaginare no_rvo è definito in un file diverso da main in modo che quando si compila main Il compilatore vedrà solo la dichiarazione

X no_rvo(X x);

e non avrò idea se l'oggetto del tipo X restituito qualunque relazione con l'argomento. Da quello che sa a quel punto, l'implementazione di no_rvo potrebbe anche essere

X no_rvo(X x) { X other; return other; }

Quindi, ad esempio, compila la linea

X const& x = no_rvo(X());

Farà quanto segue, durante l'ottimizzazione al massimo.

  • Generare la X temporanea da passare no_rvo come argomento
  • chiamata no_rvo, e legare il suo valore di ritorno a x
  • distrutto l'oggetto temporaneo a cui è passato no_rvo.

Ora se il valore di ritorno da no_rvo Sarebbe lo stesso oggetto dell'oggetto passato ad esso, quindi la distruzione dell'oggetto temporaneo significherebbe la distruzione dell'oggetto restituito. Ma ciò sarebbe sbagliato perché l'oggetto restituito è legato a un riferimento, estendendo quindi la sua vita oltre tale affermazione. Tuttavia, semplicemente non distruggere l'argomento non è una soluzione perché sarebbe sbagliato se la definizione di no_rvo è l'implementazione alternativa che ho mostrato sopra. Pertanto, se la funzione è consentita di riutilizzare un argomento come valore di ritorno, possono verificarsi situazioni in cui il compilatore non è stato possibile determinare il comportamento corretto.

Si noti che con le implementazioni comuni, il compilatore non sarebbe in grado di ottimizzarlo comunque, quindi non è una perdita così grande che non è formalmente consentita. Si noti inoltre che il compilatore è autorizzato a ottimizzare comunque la copia se può dimostrare che ciò non porta a un cambiamento nel comportamento osservabile (la cosiddetta regola as-if).

Altri suggerimenti

La solita implementazione di RVO è che il codice chiamante passa l'indirizzo di un pezzo di memoria in cui la funzione dovrebbe costruire il suo oggetto di risultato.

Quando il risultato della funzione è direttamente una variabile automatica che non è un argomento formale, che quella variabile locale può essere semplicemente inserita nel blocco di memoria fornito dal chiamante e l'istruzione di ritorno non fa alcuna copia.

Per un argomento approvato dal valore, il codice della macchina da chiamata deve copiare inizializzare il suo argomento effettivo nella posizione dell'argomento formale prima di saltare alla funzione. Affinché la funzione posiziona il suo risultato, dovrebbe prima distruggere l'oggetto argomento formale, che ha alcuni casi speciali difficili (ad esempio, quando quella costruzione si riferisce direttamente o indirettamente all'oggetto argomento formale). Pertanto, invece di identificare la posizione del risultato con la posizione dell'argomento formale, un'ottimizzazione qui deve logicamente utilizzare un blocco di memoria fornito separato per il risultato della funzione.

Tuttavia, un risultato di funzione che non viene passato in un registro è Normalmente fornito dal chiamante. Cioè ciò di cui si potrebbe ragionevolmente parlare come rvo, una specie di rvo ridotto, nel caso di a return L'espressione che indica un argomento formale è ciò che accadrebbe comunque. E non si adatta al testo "costruendo l'oggetto automatico direttamente nel valore di ritorno della funzione".

Riassumendo, il flusso di dati che richiede che il chiamante passi in un valore, significa necessariamente il chiamante che inizializza l'archiviazione di un argomento formale e non la funzione. Quindi, non è possibile evitare copiare indietro da un argomento formale in generale (Quel termine di weasel copre i casi speciali in cui il compilatore può fare cose molto speciali, in particolare per il codice della macchina inserita). Tuttavia, è la funzione che inizializza qualsiasi altro oggetto automatico locale, e quindi non è un problema fare RVO.

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