web-dev-qa-db-fra.com

Quelle est la bonne façon d'utiliser la plage de C ++ 11?

Quelle est la bonne façon d'utiliser for basé sur la plage de C++ 11?

Quelle syntaxe faut-il utiliser? for (auto elem : container) ou for (auto& elem : container) ou for (const auto& elem : container)? Ou un autre?

174
Mr.C64

Il n'y a pas de manière correcte d'utiliser for (auto elem : container) ou for (auto& elem : container) ou for (const auto& elem : container). Vous venez d'exprimer ce que vous voulez.

Laissez-moi élaborer sur cela. Faisons une promenade.

for (auto elem : container) ...

Celui-ci est un sucre syntaxique pour:

for(auto it = container.begin(); it != container.end(); ++it) {

    // Observe that this is a copy by value.
    auto elem = *it;

}

Vous pouvez utiliser celui-ci si votre conteneur contient des éléments peu coûteux à copier.

for (auto& elem : container) ...

Celui-ci est un sucre syntaxique pour:

for(auto it = container.begin(); it != container.end(); ++it) {

    // Now you're directly modifying the elements
    // because elem is an lvalue reference
    auto& elem = *it;

}

Utilisez cette option lorsque vous souhaitez écrire directement sur les éléments du conteneur, par exemple.

for (const auto& elem : container) ...

Celui-ci est un sucre syntaxique pour:

for(auto it = container.begin(); it != container.end(); ++it) {

    // You just want to read stuff, no modification
    const auto& elem = *it;

}

Comme le dit le commentaire, juste pour lire. Et c'est à peu près tout, tout est "correct" lorsqu'il est utilisé correctement.

12
user2266240

Le bon moyen est toujours

for(auto&& elem : container)

Cela garantira la préservation de toute la sémantique.

6
Puppy

Bien que la motivation initiale de la boucle plage-pour-ait pu être la facilité d'itération sur les éléments d'un conteneur, la syntaxe est suffisamment générique pour être utile même pour des objets qui ne sont pas uniquement des conteneurs.

La condition syntaxique de la boucle for est que range_expression prenne en charge begin() et end() en tant que fonctions - en tant que fonctions membres du type évalué ou en tant que fonctions non membres prenez une instance du type.

A titre d'exemple, on peut générer une plage de nombres et effectuer une itération sur la plage en utilisant la classe suivante.

struct Range
{
   struct Iterator
   {
      Iterator(int v, int s) : val(v), step(s) {}

      int operator*() const
      {
         return val;
      }

      Iterator& operator++()
      {
         val += step;
         return *this;
      }

      bool operator!=(Iterator const& rhs) const
      {
         return (this->val < rhs.val);
      }

      int val;
      int step;
   };

   Range(int l, int h, int s=1) : low(l), high(h), step(s) {}

   Iterator begin() const
   {
      return Iterator(low, step);
   }

   Iterator end() const
   {
      return Iterator(high, 1);
   }

   int low, high, step;
}; 

Avec la fonction main suivante,

#include <iostream>

int main()
{
   Range r1(1, 10);
   for ( auto item : r1 )
   {
      std::cout << item << " ";
   }
   std::cout << std::endl;

   Range r2(1, 20, 2);
   for ( auto item : r2 )
   {
      std::cout << item << " ";
   }
   std::cout << std::endl;

   Range r3(1, 20, 3);
   for ( auto item : r3 )
   {
      std::cout << item << " ";
   }
   std::cout << std::endl;
}

on obtiendrait la sortie suivante.

1 2 3 4 5 6 7 8 9 
1 3 5 7 9 11 13 15 17 19 
1 4 7 10 13 16 19 
1
R Sahu