=====Boucle sur des données (old style)===== ====Conteneur supportant les index==== std::array arr { 10, 20, 30, 40 }; for (size_t i = 0; i < arr.size(); i++) std::cout << arr[i] << std::endl; ====Conteneur supportant les itérateurs==== std::vector vector{ 10, 20, 30, 40 }; // Lecture / écriture std::vector::iterator it; for (it = vector.begin(); it != vector.end(); it++) (*it)++; // Lecture seule std::vector::const_iterator itc; for (itc = vector.cbegin(); itc != vector.cend(); itc++) std::cout << *itc << std::endl; =====Programmation fonctionnelle===== ====for each==== ===Conteneur de type primitif=== std::array arr = { 10, 20, 30, 40 }; // Ici, on utilise const & pour conserver le style fonctionnel. // Mais passer en copie un type primitif n'est pas plus penalisant que d'utiliser une référence. for (const int& i : arr) std::cout << i << std::endl; ===Conteneur de type non primitif=== std::set set { "10", "20", "30", "40" }; for (const std::string& val : set) std::cout << val << std::endl; ===Conteneur multi-types (map, tuple, ...)=== std::map map { {10, 10}, {20, 20}, {30, 30}, {40, 40} }; // L'utilisation de auto est obligatoire. // const s'applique sur toutes les variables. for (const auto & [key, value] : map) std::cout << value << std::endl; L'interprétation par le compilateur sera : std::map map = std::map, std::allocator > >{std::initializer_list >{std::pair{10, 10}, std::pair{20, 20}, std::pair{30, 30}, std::pair{40, 40}}, std::less(), std::allocator >()}; { std::map, std::allocator > > & __range1 = map; std::_Rb_tree_iterator > __begin1 = __range1.begin(); std::_Rb_tree_iterator > __end1 = __range1.end(); for(; __begin1.operator!=(__end1); __begin1.operator++()) { const std::pair & __operator9 = __begin1.operator*(); std::tuple_element<0, const std::pair >::type& key = std::get<0UL>(__operator9); std::tuple_element<1, const std::pair >::type& value = std::get<1UL>(__operator9); std::cout.operator<<(value).operator<<(std::endl); } } ===Implémentation sur une classe personnalisée=== Il faut définir l'itérateur et la classe à parcourir. [[https://www.cprogramming.com/c++11/c++11-ranged-for-loop.html|C++11 range-based for loops]] {{ :lang:cpp:boucles:range-based_for_loops_in_c_11_-_cprogramming.com_2019-12-26_12_35_03_.html |Archive le 26/12/2019}} Dans l'idéal, ''DataSample'' devrait prendre une classe en ''template'' plutôt que la classe ''Data'' en dur. Il faut commencer par déclarer l'itérateur. #include class DataSample; // Données accessibles depuis la boucle. class Data { public: int getA() const { return a; } void setA(int aa) { a = aa; } private: int a; }; // Itérateur accessible en écriture. class Iter { public: Iter(DataSample &p_vec, int pos) : _pos(pos), _p_vec(p_vec) {} bool operator!=(const Iter &other) const { return _pos != other._pos; } Data &operator*(); Iter &operator++() { ++_pos; return *this; } private: int _pos; DataSample &_p_vec; }; // Iterateur accessible uniquement en lecture. class ConstIter { public: ConstIter(const DataSample &p_vec, int pos) : _pos(pos), _p_vec(p_vec) {} bool operator!=(const ConstIter &other) const { return _pos != other._pos; } const Data &operator*() const; const ConstIter &operator++() { ++_pos; return *this; } private: int _pos; const DataSample &_p_vec; }; // Classe stockant les données et implémentant le pattern for each. class DataSample { public: // Méthodes accessibles en écriture. Data &get(int col) { return _data[col]; } Iter begin() { return Iter(*this, 0); } Iter end() { return Iter(*this, 100); } // Méthodes accessibles uniquement en lecture seule. const Data &get(int col) const { return _data[col]; } ConstIter begin() const { return ConstIter(*this, 0); } ConstIter end() const { return ConstIter(*this, 100); } void set(int index, int val) { _data[index].setA(val); } private: // Dans cette classe, les données sont sous forme d'un tableau. Data _data[100]; }; Data &Iter::operator*() { return _p_vec.get(_pos); } const Data &ConstIter::operator*() const { return _p_vec.get(_pos); } // Exemple d'usage. int main() { DataSample v; for (int i = 0; i < 100; i++) { v.set(i, i); } // Utilisation de begin et end en non const. // Le type de retour est Data& (qui peut être casté en const Data&). for (Data &i : v) { std::cout << i.getA() << std::endl; } // Utilisation de begin et end en const. // Le type de retour est obligatoirement const Data&. for (const Data &i : static_cast(v)) { std::cout << i.getA() << std::endl; } } ====Ranges==== Les ''ranges'' de la ''std'' n'implémentent que des algorithmes en $O(1)$. Il n'est donc pas possible de trier des vues. * Boucle #include for (int i : std::views::iota(0, 100)) std::cout << i << std::endl; * Pour faire la boucle à l'envers : #include for (int i : std::views::iota(0, 100) | std::views::reverse) std::cout << i << std::endl; * S'arrêter sous une certaine condition : #include for (int i : std::views::iota(0, 100) | std::views::take_while([](int i){ return 0 <= i && i <= 15; })) std::cout << i << std::endl; * Filter les valeurs : #include for (int i : std::views::iota(0, 100) | std::views::filter([](int i){ return 50 <= i && i <= 66; })) std::cout << i << std::endl; * Modifier les valeurs dans la vue : #include int main() { for (const std::string& i : // Integer std::views::iota(0, 100) | // const char * std::views::transform([](int i) { if (i % 15 == 0) return "FooBar\n"; else if (i % 3 == 0) return "Foo\n"; else if (i % 5 == 0) return "Bar\n"; else return ""; })) std::cout << i << std::endl; } Les vues sont en lecture seule Le code int main() { std::vector v {1, 2, 3, 4, 5, 6, 7, 8, 9}; for (int & i : v | std::views::take(5)) i = 3; for (const int & i : v) std::cout << i << "\n"; } affichera 1 2 3 4 5 6 7 8 9 * Ne prendre que les premiers résultats #include int main() { for (int i : std::views::iota(0, 100) | std::views::take(10)) std::cout << i << std::endl; } =====Via une coroutine===== #include #include std::experimental::generator loop(int iterations) { for (int i = 0; i < iterations; i++) { co_yield i; } } int main() { for (int i : loop(100)) { std::cout << i << std::endl; } }