Loop baseado no intervalo C ++ 11: obtém item por valor ou referência a const

215

Lendo alguns exemplos de loops baseados em intervalo, eles sugerem duas maneiras principais 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 
}

ou

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

Bem.

Quando não precisamos alterar vecitens, os Exemplos da IMO sugerem o uso da segunda versão (por valor). Por que eles não sugerem algo que constfaça referência (pelo menos eu não encontrei nenhuma sugestão direta):

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 
}

Não é melhor? Não evita uma cópia redundante em cada iteração enquanto é const?

masoud
fonte

Respostas:

390

Se você não deseja alterar os itens e evitar fazer cópias, auto const &é a escolha correta:

for (auto const &x : vec)

Quem sugere que você use auto &está errado. Ignore-os.

Aqui está o resumo:

  • Escolha auto xquando deseja trabalhar com cópias.
  • Escolha auto &xquando deseja trabalhar com itens originais e pode modificá-los.
  • Escolha auto const &xquando deseja trabalhar com itens originais e não os modificará.
Nawaz
fonte
21
Obrigado pela ótima resposta. Eu acho que também deve ser apontado que const auto &xé equivalente à sua terceira escolha.
18715 smg #
7
@mloskot: É equivalente. (eo que você quer dizer com "mas é a mesma sintaxe" A sintaxe é observável diferente?.)
Nawaz
14
Ausente: auto&&quando você não deseja fazer uma cópia desnecessária e não se importa se a modifica ou não, e deseja apenas trabalhar.
Yakk - Adam Nevraumont
4
A distinção de referência se aplica às cópias se você estiver apenas trabalhando com tipos fundamentais como int / double?
4
@racarate: Não posso comentar sobre a velocidade geral e acho que ninguém pode, sem traçar um perfil primeiro. Francamente, não vou basear minha escolha na velocidade, mas na clareza do código. Se eu quero imutabilidade, eu usaria constcom certeza. No entanto, se seria auto const &, ou auto const tem pouca diferença. Eu escolheria auto const &apenas ser mais consistente.
Nawaz
23

Se você tem um std::vector<int>ou std::vector<double>, é bom usar auto(com cópia de valor) em vez de const auto&, pois copiar um intou a doubleé barato:

for (auto x : vec)
    ....

Mas se você tiver um std::vector<MyClass>, onde MyClasstenha alguma semântica de cópia não trivial (por exemplo std::string, alguma classe personalizada complexa etc.), sugiro usar const auto&para evitar cópias profundas :

for (const auto & x : vec)
    ....
Mr.C64
fonte
3

Quando não precisamos alterar vecitens, os Exemplos sugerem o uso da primeira versão.

Então eles dão uma sugestão errada.

Por que eles não sugerem algo que const faz referência

Porque eles dão uma sugestão errada :-) O que você mencionou está correto. Se você deseja apenas observar um objeto, não há necessidade de criar uma cópia e não há uma constreferência a ele.

EDITAR:

Vejo que as referências que você vincula fornecem exemplos de iterações sobre um intervalo de intvalores ou algum outro tipo de dados fundamental. Nesse caso, uma vez que copiar um intnão é caro, criar uma cópia é basicamente equivalente a (se não for mais eficiente do que) ter uma observação const &.

No entanto, esse não é o caso em geral para tipos definidos pelo usuário. UDTs pode ser caro para copiar e, se você não tiver um motivo para criar uma cópia (como modificar o objeto recuperado sem alterar o original), é preferível usar a const &.

Andy Prowl
fonte
1

Vou ser contrário aqui e dizer que não há necessidade de auto const &um intervalo baseado em loop. Diga-me se você acha que a seguinte função é boba (não em seu propósito, mas na maneira como está escrita):

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;
}

Aqui, o autor criou uma referência const vpara usar em todas as operações que não modificam v. Isso é bobagem, na minha opinião, e o mesmo argumento pode ser usado para usar auto const &como variável em um intervalo baseado em loop em vez de apenas auto &.

Benjamin Lindley
fonte
3
@BenjaminLindley: Então posso deduzir que você também argumentaria contra const_iteratorem um loop for? Como garantir que você não altere os itens originais de um contêiner ao iterá-lo?
Nawaz
2
@BenjaminLindley: Por que seu código não seria compilado sem const_iterator? Deixe-me adivinhar, o contêiner sobre o qual você interage é const. Mas por que é constpara começar? Em algum lugar que você está usando constpara garantir o que?
Nawaz
8
A vantagem de criar objetos consté como as vantagens de usar private/ protectednas classes. Evita mais erros.
masoud
2
@BenjaminLindley: Oh, eu ignorei isso. Nesse caso, a implementação também é boba. Mas se essa função não fosse modificada v, a implementação seria boa para a IMO. E se o loop nunca modifica os objetos pelos quais itera, parece-me correto fazer uma ref const. Entendo seu ponto de vista. O meu é que o loop representa uma unidade de código da mesma forma que a função e, dentro dessa unidade, não há instruções que precisem modificar o valor. Portanto, você pode "marcar" a unidade inteira como const, como faria com uma constfunção de membro.
Andy Prowl
1
Então, você acha const &que o baseado em intervalo é tolo, porque você escreveu um exemplo imaginário irrelevante de um programador imaginário que fez algo tolo com a qualificação cv ? ... OK então
underscore_d