Sposta la semantica in C ++ - Sposta-ritorno delle variabili locali


10

La mia comprensione è che in C ++ 11, quando si restituisce una variabile locale da una funzione in base al valore, al compilatore è consentito trattare quella variabile come riferimento di valore r e 'spostarla' dalla funzione per restituirla (se RVO / NRVO non accade invece, ovviamente).

La mia domanda è: non è possibile interrompere il codice esistente?

Considera il seguente codice:

#include <iostream>
#include <string>

struct bar
{
  bar(const std::string& str) : _str(str) {}
  bar(const bar&) = delete;
  bar(bar&& other) : _str(std::move(other._str)) {other._str = "Stolen";}
  void print() {std::cout << _str << std::endl;}

  std::string _str;
};

struct foo
{
  foo(bar& b) : _b(b) {}
  ~foo() {_b.print();}

  bar& _b;
};

bar foobar()
{
  bar b("Hello, World!");
  foo f(b);

  return std::move(b);
}

int main()
{
  foobar();
  return EXIT_SUCCESS;
}

Pensavo che sarebbe stato possibile per un distruttore di un oggetto locale fare riferimento all'oggetto che viene implicitamente spostato, e quindi inaspettatamente vedere un oggetto "vuoto". Ho provato a testare questo (vedi http://ideone.com/ZURoeT ), ma ho ottenuto il risultato 'corretto', senza l'esplicita std::movein foobar(). Immagino che ciò sia dovuto a NRVO, ma non ho provato a riorganizzare il codice per disabilitarlo.

Sono corretto in quanto questa trasformazione (causando uno spostamento dalla funzione) avviene implicitamente e potrebbe violare il codice esistente?

AGGIORNAMENTO Ecco un esempio che illustra di cosa sto parlando. I seguenti due collegamenti sono per lo stesso codice. http://ideone.com/4GFIRu - C ++ 03 http://ideone.com/FcL2Xj - C ++ 11

Se guardi l'output, è diverso.

Quindi, suppongo che questa domanda diventi adesso, è stata presa in considerazione quando si è aggiunta la mossa implicita allo standard, ed è stato deciso che era giusto aggiungere questa modifica di rottura poiché questo tipo di codice è abbastanza raro? Mi chiedo anche se qualche compilatore avvertirà in casi come questo ...


Una mossa deve sempre lasciare l'oggetto in uno stato distruttibile.
Zan Lynx,

Sì, ma non è questa la domanda. Il codice pre-c ++ 11 potrebbe presumere che il valore di una variabile locale non cambierebbe semplicemente a causa della sua restituzione, quindi questa mossa implicita potrebbe infrangere tale presupposto.
Bwmat,

Questo è ciò che ho cercato di chiarire nel mio esempio; tramite i distruttori è possibile controllare lo stato di (un sottoinsieme di) variabili locali di una funzione "dopo" l'esecuzione dell'istruzione return, ma prima che la funzione effettivamente ritorni.
Bwmat,

Questa è un'ottima domanda con l'esempio che hai aggiunto. Spero che questo ottenga più risposte dai professionisti che possono chiarire questo. L'unico vero feedback che posso dare è: questo è il motivo per cui gli oggetti di solito non dovrebbero avere viste non proprietarie nei dati. In realtà ci sono molti modi per scrivere codice dall'aspetto innocente che segfault quando si danno agli oggetti viste non proprietarie (puntatori o riferimenti non elaborati). Posso approfondire la questione con una risposta adeguata, se lo desideri, ma suppongo che non sia ciò di cui vuoi davvero sentire parlare. E tra l'altro, è già noto che 11 può violare il codice esistente, ad esempio definendo nuove parole chiave.
Nir Friedman,

Sì, lo so che C ++ 11 non ha mai preteso di non violare alcun vecchio codice, ma questo è abbastanza sottile e sarebbe davvero facile perdersi (nessun errore del compilatore, avvisi, segfault)
Bwmat

Risposte:


8

Scott Meyers ha pubblicato su comp.lang.c ++ (agosto 2010) su un problema in cui la generazione implicita di costruttori di mosse potrebbe spezzare gli invarianti di classe C ++ 03:

struct X
{
  // invariant: v.size() == 5
  X() : v(5) {}

  ~X() { std::cout << v[0] << std::endl; }

private:    
  std::vector<int> v;
};

int main()
{
    std::vector<X> y;
    y.push_back(X()); // X() rvalue: copied in C++03, moved in C++0x
}

Qui il problema è che in C ++ 03 Xaveva un invariante che il suo vmembro aveva sempre 5 elementi. X::~X()contava su quell'invariante, ma il costruttore di mosse appena introdotto si spostava v, impostando così la sua lunghezza su zero.

Questo è correlato al tuo esempio poiché l'invariante rotto viene rilevato solo nel Xdistruttore (come dici tu è possibile che un distruttore di un oggetto locale faccia riferimento all'oggetto che viene implicitamente spostato e quindi vede inaspettatamente un oggetto vuoto ).

C ++ 11 tenta di raggiungere un equilibrio tra la rottura di parte del codice esistente e la fornitura di utili ottimizzazioni basate sui costruttori di spostamento.

Inizialmente il comitato ha deciso che i costruttori di mosse e gli operatori di assegnazione di mosse dovrebbero essere generati dal compilatore quando non forniti dall'utente.

Decise quindi che ciò era effettivamente causa di allarme e limitò la generazione automatica di costruttori di mosse e operatori di assegnazione di mosse in modo tale che è molto meno probabile, sebbene non impossibile, che il codice esistente si rompesse (ad esempio un distruttore definito esplicitamente).

È allettante pensare che prevenire la generazione di costruttori di mosse implicite quando è presente un distruttore definito dall'utente sia sufficiente, ma non è vero ( N3153 - La mossa implicita deve andare per ulteriori dettagli).

In N3174 - Per spostare o non spostare Stroupstrup dice:

Considero questo un problema di progettazione del linguaggio, piuttosto che un semplice problema di compatibilità con le versioni precedenti. È facile evitare di rompere il vecchio codice (ad esempio basta rimuovere le operazioni di spostamento da C ++ 0x), ma vedo rendere C ++ 0x un linguaggio migliore rendendo le operazioni di spostamento pervasive un obiettivo principale per il quale potrebbe valere la pena infrangere alcuni C + +98 codice.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.