web-dev-qa-db-fra.com

Boucle basée sur une plage C ++ 11: obtient un élément par valeur ou une référence à const

En lisant quelques exemples de boucles basées sur la plage, ils suggèrent deux manières principales 1 , 2 , , 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
}

Bien.

Lorsque nous n'avons pas besoin de changer les éléments vec, IMO, Exemples suggèrent d'utiliser la deuxième version (par valeur). Pourquoi ne suggèrent-ils pas quelque chose auquel const fait référence (du moins, je n’ai trouvé aucune suggestion directe):

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'est-ce pas mieux? Cela évite-t-il une copie redondante à chaque itération alors qu'il s'agit d'un const?

186
deepmax

Si vous ne voulez pas changer les éléments ni vouloir éviter faire des copies, alors auto const & est le bon choix:

for (auto const &x : vec)

Celui qui vous suggère d'utiliser auto & a tort. Ignore les.

Voici récapitulation:

  • Choisissez auto x lorsque vous souhaitez utiliser des copies.
  • Choisissez auto &x lorsque vous souhaitez utiliser les éléments d'origine et que vous pouvez les modifier.
  • Choisissez auto const &x lorsque vous souhaitez utiliser les éléments d'origine sans les modifier.
337
Nawaz

Si vous avez un std::vector<int> ou std::vector<double>, alors c'est bien d'utiliser auto (avec une copie de valeur) au lieu de const auto&, car copier un int ou un double n'est pas cher:

for (auto x : vec)
    ....

Mais si vous avez un std::vector<MyClass>, où MyClass possède une sémantique de copie non triviale (par exemple, std::string, une classe personnalisée complexe, etc.), alors je vous suggérerais d'utiliser const auto& pour éviter les copies profondes:

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

Lorsque nous n’avons pas besoin de changer les éléments vec, des exemples suggèrent d’utiliser la première version.

Ensuite, ils donnent une mauvaise suggestion.

Pourquoi ils ne suggèrent pas quelque chose qui référence const

Parce qu'ils donnent une mauvaise suggestion :-) Ce que vous mentionnez est correct. Si vous ne voulez que observer un objet, il n’est pas nécessaire de créer une copie, et il n’est pas nécessaire d’avoir une référence non -const.

EDIT:

Je vois que les références que vous associez fournissent toutes des exemples d’itérations sur une plage de valeurs int ou sur un autre type de données fondamental. Dans ce cas, comme la copie d'une int n'est pas chère, créer une copie est fondamentalement équivalent à (sinon plus efficace) que d'avoir un const & d'observation.

Ce n'est toutefois pas le cas en général pour les types définis par l'utilisateur. Les UDT peuvent être coûteux à copier, et si vous n'avez pas de raison de créer une copie (telle que modifier l'objet récupéré sans modifier celui d'origine), il est préférable d'utiliser un const &.

3
Andy Prowl

Je vais être contraire ici et dire qu'il n'y a pas besoin de auto const & dans une plage basée sur une boucle. Dites-moi si vous pensez que la fonction suivante est stupide (pas dans son but, mais dans la façon dont elle est écrite):

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

Ici, l’auteur a créé une référence constante à v à utiliser pour toutes les opérations ne modifiant pas v. C’est idiot, à mon avis, et le même argument peut être avancé pour utiliser auto const & variable dans une plage basée sur la boucle for au lieu de simplement auto &.

1
Benjamin Lindley