2012-01-03 13 views
83

Qual è lo scopo del const in questo?Scopo del ritorno con valore const?

const Object myFunc(){ 
    return myObject; 
} 

Ho appena iniziato a leggere Effective C++ e Articolo 3 sostenitori questa e una ricerca di Google raccoglie i suggerimenti simili, ma anche esempi contrari. Non riesco a vedere come usare cost qui sarebbe sempre preferibile. Supponendo che un ritorno per valore sia desiderabile, non vedo alcun motivo per proteggere il valore restituito. L'esempio fornito per il motivo per cui ciò potrebbe essere utile è la prevenzione dei cast di bool involontari del valore di ritorno. Il vero problema è che i cast di bool impliciti dovrebbero essere prevenuti con la parola chiave esplicita.

L'utilizzo di cost qui impedisce l'utilizzo di oggetti temporanei senza assegnazione. Quindi non potevo eseguire espressioni aritmetiche con quegli oggetti. Non sembra che ci sia mai un caso in cui un const senza nome sia utile.

Cosa si ottiene utilizzando const qui e quando sarebbe preferibile?

MODIFICA: consente di modificare l'esempio aritmetico per qualsiasi funzione che modifichi un oggetto che si desidera eseguire prima di un compito.

+1

Sì, è possibile eseguire operazioni aritmetiche con oggetti const poiché gli operatori aritmetici devono essere const e restituire anche oggetti const –

risposta

87

Nella ipotetica situazione in cui è possibile eseguire un potenzialmente costosa operazione non-const su un oggetto, di ritorno da const-valore che impedisce di accidentalmente chiamando questa operazione su un temporaneo. Immaginate che + restituito un valore non-const, e si potrebbe scrivere:

(a + b).expensive(); 

Nell'era di C++ 11, tuttavia, si consiglia vivamente di restituire valori come non-const in modo da poter trarre il massimo vantaggio dei riferimenti rvalue, che hanno senso solo sui rvalori non costanti.

In sintesi, lo è un razionale per questa pratica, ma è essenzialmente obsoleto.

+0

Bene, Herb Sutter [valori costanti di ritorno raccomandati] (http://books.google.com/books?id = 58rZvOSuheEC & lpg = PA179 & ots = L_LgismXMo & dq = sutter% 20return% 20const% 20valore & pg = PA179 # v = onepage & q & f = false) per tipi non primitivi, ma penso che tu abbia ragione che il consiglio sia ormai obsoleto. –

+10

@FredLarson: Sì, in un libro di 14 anni: -S –

+0

Questa risposta implica che la restituzione per valore const significa const &&, ma è proprio vero ?! per esempio. VS13 mi permette di associare int && var = a una funzione che restituisce const int. "Il risultato di chiamare una funzione il cui tipo restituito non è un riferimento è un valore". –

32

It's prettypointless per restituire un valore const da una funzione.

E 'difficult per farlo ha alcun effetto sul vostro codice:

const int foo() { 
    return 3; 
} 

int main() { 
    int x = foo(); // copies happily 
    x = 4; 
} 

and:

const int foo() { 
    return 3; 
} 

int main() { 
    foo() = 4; // not valid anyway for built-in types 
} 

// error: lvalue required as left operand of assignment 

Anche se you can notice if the return type is a user-defined type:

struct T {}; 

const T foo() { 
    return T(); 
} 

int main() { 
    foo() = T(); 
} 

// error: passing ‘const T’ as ‘this’ argument of ‘T& T::operator=(const T&)’ discards qualifiers 

è discutibile se questo è di alcun beneficio per chiunque.

Il ritorno di un riferimento è diverso, ma a meno che lo Object non sia un parametro del modello, non lo farai.

+0

Si noti che il secondo esempio attiva solo un errore per i tipi predefiniti. – Xeo

+0

Puoi approfondire il primo esempio? Non ha alcun senso per me dire che il valore di ritorno sarà const, ma essere in grado di assegnarlo a una variabile non const. –

+1

@IvayloToskov: tutta l'elaborazione di cui hai bisogno è _in_ l'esempio, in particolare il commento che dice "copia felicemente". Si consideri 'const int x = 4; int y = x; 'che è anche perfettamente a posto. –

-2

Può essere utilizzato come funzione wrapper per restituire un riferimento a un tipo di dati di costante privata. Ad esempio, in un elenco collegato si hanno le costanti in coda e in testa e, se si desidera determinare se un nodo è un nodo di coda o di testa, è possibile confrontarlo con il valore restituito da tale funzione.

Anche se ogni ottimizzatore molto probabilmente ottimizzare fuori comunque ...

+0

Ma sta restituendo 'Oggetto'. –

+0

Non c'è niente da ottimizzare, const-ness è un meccanismo di sicurezza in fase di compilazione. –

+0

Ah, l'ho letto male come Oggetto * ... –

-3

myObject potrebbe essere un puntatore. Il "const" sta proteggendo il valore puntato da myObject.

+3

In realtà, no. 'Object const *' non è 'Object * const'. –

3

Si assicura che l'oggetto restituito (che è un valore RValue in quel punto) non possa essere modificato. Questo fa in modo che l'utente non può fare pensa in questo modo:

myFunc() = Object(...); 

che avrebbe funzionato bene se myFunc restituito da riferimento, ma è quasi certamente un bug quando tornò per valore (e probabilmente non sarà catturato dalla compilatore). Naturalmente in C++ 11 con i suoi rvalues ​​questa convenzione non ha più senso come in precedenza, dal momento che non è possibile spostare un oggetto const, quindi questo può avere effetti piuttosto pesanti sulle prestazioni.

+0

Questo non spiega il 'const'. –

+0

@Nicol Bolas: come non spiega il const? L'esempio di codice durante la compilazione se il tipo restituito è 'Object', ma non se è' const Object' – Grizzly

+0

Presumibilmente si intende "in C++ 11 con i suoi _xvalues_"? (Sapendo che _xvalues_ e _prvalues_ sono entrambi _rvalues_, ma sono gli _xvalues_ che sono nuovi in ​​C++ 11 che sono la differenza importante.) –

1

C++ 11 lo rende piuttosto utile con gli oggetti solo spostamento. Ad esempio:

const std::unique_ptr<T> myFunc(); 

unique_ptr è un tipo che non può essere copiata; può essere spostato solo Ma non è possibile chiamare std::move su un tipo const. Pertanto, l'unico modo per conservare questo è con un const& o const&&:

const std::unique_ptr<T> &ptr = myFunc(); 

Poiché è un const unique_ptr, esso non può essere spostato da. Né può essere copiato da. Il che significa che è molto difficile da memorizzare in realtà ovunque a lungo termine. Puoi metterlo in pila. Ma renderlo membro di una classe è impossibile (senza invocare comportamenti indefiniti).

In questo modo, è possibile assicurare che il puntatore non venga memorizzato a lungo termine. Ciò consente di creare una versione specializzata di unique_ptr chi cancella effettivamente non cancella la memoria. In questo modo, una funzione può restituire un puntatore sapendo che l'utente non può memorizzarlo da nessuna parte.

Ovviamente, ciò rende anche piuttosto difficile per il chiamante restituire il valore. Quindi ci sono aspetti negativi.

+1

Mi spiace, ma devo downvotare questo - prima, * puoi * memorizzare la variabile lungo -term - in particolare in una variabile globale o funzione-statica o thread-locale. In secondo luogo, anche se il valore di rvalue è associato a un riferimento locale, non significa che quella funzione non verrà eseguita fino alla fine del programma. Terzo, rendere la vita del chiamante più difficile è una cosa estremamente stupida da chiedere. E in quarto luogo, fare un 'unique_ptr' che non cancella il suo pointee è possibile - devi solo assicurarti che il pointee sopravviva al puntatore (ad esempio è un globale). Il tuo dispositivo non è d'aiuto con questa garanzia. – jpalecek

+0

Attendi wat? Stai memorizzando un riferimento a un temporaneo che viene immediatamente distrutto? Compilare anche questo? –

+2

@EmilyL. Un riferimento const a un temporaneo può estenderne la durata, vedere https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/ – tomjakubowski

Problemi correlati