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
?
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:
auto x
lorsque vous souhaitez utiliser des copies.auto &x
lorsque vous souhaitez utiliser les éléments d'origine et que vous pouvez les modifier.auto const &x
lorsque vous souhaitez utiliser les éléments d'origine sans les modifier.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)
....
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 &
.
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 &
.