Domanda

Qual è la progettazione logica alla base permettendo a questa

const Foo& a = function_returning_Foo_by_value();

ma non questa

Foo& a = function_returning_Foo_by_value();

Cosa potrebbe possibile sbagliato andare nella seconda riga (che non sarebbe già andare male nella prima riga)?

È stato utile?

Soluzione

Risponderò alla tua domanda ... viceversa.

Perché hanno permesso Foo const& foo = fooByValue(); per cominciare?

Si rende la vita (un po ') più facile, ma introduce il potenziale comportamento non definito in tutto il luogo.

Foo const& fooByReference()
{
  return fooByValue(); // error: returning a reference to a temporary
}

Questa è ovviamente sbagliato, e in effetti il ??compilatore doverosamente segnalarlo. Come per il commento di Tomalak: non è dato mandato dalla norma, ma buoni compilatori dovrebbe segnalarlo. Clang, gcc e MSVC fanno. Penso che Comeau e ICC sarebbe troppo.

Foo const& fooByIndirectReference()
{
  Foo const& foo = fooByValue(); // OK, you're allowed to bind a temporary
  return foo;                    // Generally accepted
}

Questo è sbagliato, ma è più sottile. Il problema è che la durata della temporanea è legato alla durata di vita foo, che esula dall'ambito alla fine della funzione. Un Copia di foo viene passato al chiamante, e la copia punti nell'etere.

ho sollevato il bug su Clang, e Argyris è stato in grado di diagnosticare questo caso (complimenti davvero: p).

Foo const& fooForwarder(Foo const&); // out of line implementation which forwards
                                     // the argument

Foo const& fooByVeryIndirectReference()
{
  return fooForwarder(fooByValue());
}

La temporanea creata da fooByValue è legato alla durata della tesi di fooForwarder, che doverosamente fornire una copia (di riferimento), copia che viene restituito al chiamante, anche se ora i punti nell'etere.

Il problema qui è che la realizzazione di fooForwarder è perfettamente bene WRT lo standard, eppure crea un comportamento indefinito nel suo chiamante.

Il fatto scoraggiante, però, è che la diagnosi di questo richiede la conoscenza circa l'attuazione di fooForwarder, che è fuori portata per il compilatore.

L'unica soluzione che riesco a capire (a parte WPA) è una soluzione di runtime: ogni volta che una temporanea è delimitata a un punto di riferimento, quindi è necessario fare in modo che il riferimento restituito non condivide lo stesso indirizzo ... e poi cosa ? assert? sollevare un'eccezione? E poiché è solo una soluzione di runtime, è chiaramente non è soddisfacente.

L'idea di legare un temporaneo ad un riferimento è fragile.

Altri suggerimenti

ho capito la logica come segue: una temporanea dovrebbe essere distrutta quando passa nell'ambito. Se prometti di non modificarlo io vi permetterà di prolungare la sua vita.

Il motivo che i puntatori non-const non prolungano la durata di provvisori è che i riferimenti non-const non può essere vincolata alla provvisori, in primo luogo.

Ci sono molte ragioni per questo, mi limiterò a mostrare un esempio classico che coinvolge le conversioni implicite allargamento:

struct Foo {};
bool CreateFoo( Foo*& result ) { result = new Foo(); return true; }

struct SpecialFoo : Foo {};
SpecialFoo* p;
if (CreateFoo(p)) { /* DUDE, WHERE'S MY OBJECT! */ }

Il razionale per permettere riferimenti const per provvisori bind è che consente al codice perfettamente ragionevole in questo modo:

bool validate_the_cat(const string&);

string thing[3];
validate_the_cat(thing[1] + thing[2]);

Si noti che nessuna estensione tutta la vita è stata necessaria in questo caso.

"Cosa potrebbe andare storto" è che si modifica un oggetto allora immediatamente perdere i cambiamenti, e la regola è quindi definito per aiutare non si fanno tali errori. Si potrebbe pensare che se si chiama la funzione di nuovo si otterrebbe un oggetto con le modifiche, che naturalmente si sarebbe non perché si è modificato una copia.

Il caso tipico in cui si crea una temporanea quindi chiamare un metodo non-const su di esso è quando si sta per scambiarlo:

std::string val;
some_func_that_returns_a_string().swap( val );

Questo a volte può essere molto utile.

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