Loop basato su intervallo C ++ 11: ottiene l'elemento per valore o riferimento a const


215

Leggendo alcuni esempi di loop basati su range, suggeriscono due modi principali 1 , 2 , 3 , 4

std::vector<MyClass> vec;

for (auto &x : vec)
{
  // x is a reference to an item of vec
  // We can change vec's items by changing x 
}

o

for (auto x : vec)
{
  // Value of x is copied from an item of vec
  // We can not change vec's items by changing x
}

Bene.

Quando non è necessario modificare gli vecarticoli, IMO, gli esempi suggeriscono di utilizzare la seconda versione (in base al valore). Perché non suggeriscono qualcosa a cui fa constriferimento (almeno non ho trovato alcun suggerimento diretto):

for (auto const &x : vec) // <-- see const keyword
{
  // x is a reference to an const item of vec
  // We can not change vec's items by changing x 
}

Non è meglio? Non evita una copia ridondante in ogni iterazione mentre è un const?

Risposte:


390

Se non vuoi cambiare gli articoli e vuoi evitare di fare copie, allora auto const &è la scelta giusta:

for (auto const &x : vec)

Chi ti suggerisce di usare ha auto &torto. Ignorali.

Ecco il riassunto:

  • Scegli auto xquando vuoi lavorare con le copie.
  • Scegli auto &xquando vuoi lavorare con oggetti originali e modificarli.
  • Scegli auto const &xquando vuoi lavorare con oggetti originali e non li modificherai.

21
Grazie per la magnifica risposta. Immagino che dovrebbe anche essere sottolineato che const auto &xequivale alla tua terza scelta.
smg

7
@mloskot: è equivalente. (e cosa intendi con "ma è la stessa sintassi" ? La sintassi è notevolmente diversa.)
Nawaz,

14
Manca: auto&&quando non vuoi fare una copia inutile, e non ti importa se la modifichi o no, e vuoi semplicemente lavorare.
Yakk - Adam Nevraumont,

4
La distinzione di riferimento si applica alle copie se stai solo lavorando con tipi fondamentali come int / double?

4
@racarate: non posso commentare la velocità complessiva , e penso che nessuno possa, senza prima profilarla. Francamente, non baserò la mia scelta sulla velocità, piuttosto sulla chiarezza del codice. Se voglio l'immutabilità, lo userei constdi sicuro. Tuttavia, se sarebbeauto const & , o auto const ha poca differenza. Sceglierei auto const &solo per essere più coerente.
Nawaz,

23

Se hai un std::vector<int>o std::vector<double>, allora va bene usare auto(con copia del valore) invece di const auto&, poiché copiare un into un doubleè economico:

for (auto x : vec)
    ....

Ma se hai una std::vector<MyClass>, dove MyClassha delle semantiche di copia non banali (ad es std::string. Qualche classe personalizzata complessa, ecc.) Allora suggerirei di usare const auto&per evitare copie profonde :

for (const auto & x : vec)
    ....

3

Quando non è necessario modificare gli vecarticoli, gli esempi suggeriscono di utilizzare la prima versione.

Quindi danno un suggerimento sbagliato.

Perché non suggeriscono qualcosa che costituisca riferimenti

Perché danno un suggerimento sbagliato :-) Quello che dici è corretto. Se vuoi solo osservare un oggetto, non è necessario crearne una copia e non è necessario avere un constriferimento ad esso.

MODIFICARE:

Vedo che i riferimenti che colleghi tutti forniscono esempi di iterazione su un intervallo di intvalori o altri tipi di dati fondamentali. In tal caso, poiché la copia di un intnon è costosa, la creazione di una copia equivale sostanzialmente a (se non più efficiente di) avere un'osservazione const &.

Questo, tuttavia, non è il caso in generale per i tipi definiti dall'utente. Gli UDT possono essere costosi da copiare e se non si ha un motivo per creare una copia (come la modifica dell'oggetto recuperato senza alterare quello originale), è preferibile utilizzare a const &.


1

Qui sarò contrario e dirò che non è necessario auto const &in un intervallo basato su loop. Dimmi se pensi che la seguente funzione sia sciocca (non nel suo scopo, ma nel modo in cui è scritta):

long long SafePop(std::vector<uint32_t>& v)
{
    auto const& cv = v;
    long long n = -1;
    if (!cv.empty())
    {
        n = cv.back();
        v.pop_back();
    }
    return n;
}

Qui, l'autore ha creato un riferimento const vda usare per tutte le operazioni che non modificano v. Questo è sciocco, secondo me, e lo stesso argomento può essere fatto per usare auto const &come variabile in un intervallo basato su loop invece che solo auto &.


3
@BenjaminLindley: Quindi posso dedurre che anche tu discuteresti const_iteratorin un ciclo for? Come ti assicuri di non modificare gli articoli originali da un contenitore quando esegui l'iterazione su di esso?
Nawaz

2
@BenjaminLindley: perché il tuo codice non dovrebbe essere compilato senza const_iterator? Fammi indovinare, il contenitore su cui stai iterando const. Ma perché constcominciare? Da qualche parte stai usando constper garantire cosa?
Nawaz,

8
Il vantaggio di creare oggetti constè come i vantaggi dell'uso private/ protectedin classi. Evita ulteriori errori.
masoud,

2
@BenjaminLindley: Oh, l'ho trascurato. Quindi, in questo caso, anche l'implementazione è sciocca. Ma se tale funzione non fosse modificata v, l'implementazione andrebbe bene IMO. E se il ciclo non modifica mai gli oggetti attraverso i quali scorre, mi sembra giusto avere un riferimento const. Vedo il tuo punto però. Il mio è che il loop rappresenta un'unità di codice più o meno come fa la funzione, e all'interno di quell'unità non ci sono istruzioni che devono modificare il valore. Pertanto, è possibile "taggare" l'intera unità come const, come si farebbe con una constfunzione membro.
Andy Prowl

1
Quindi pensi const &che il range-based sia sciocco, perché hai scritto un esempio immaginario irrilevante di un programmatore immaginario che ha fatto qualcosa di stupido con la certificazione CV ? ... OK allora
underscore_d
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.