C ++ 11 est génial. Lune des plus belles fonctionnalités (à mon avis) est probablement la soi-disant boucle basée sur la plage. Au lieu de
for ( std::size_t i(0); i < range.size(); ++i ) { // do something to range[i]; }
ou
for ( Range::iterator it(range.begin()); it != range.end(); ++it ) { // do something to *it; }
ou simplifié avec C ++ 11 auto:
for ( auto it(range.begin()); it != range.end(); ++it ) { // do something to *it; }
nous pouvons dire ceci:
for ( auto& entry : range ) { // do something with entry. }
Cest très expressif: nous parlons à propos de lentrée, plutôt que la i ^ ème position dans la plage ou litérateur pointant vers une entrée. Cependant, cette syntaxe na pas la capacité davoir des sous-plages (par exemple en ignorant la dernière entrée). Dans une série de petites structures / méthodes daide, je veux ajouter cette fonctionnalité dune manière propre.
Voilà pour ma motivation 😉 Maintenant, pour la vraie affaire.
Dans ce post, jaborde les conditions sur les entrées de range
. Fondamentalement, le code dassistance devrait exécuter léquivalent de
for ( auto& entry : range ) { if ( condition(entry) ) { // do something to entry. } }
mais sans ce niveau dindentation.
Voici le code pour ConditionalRange:
template <typename Range, typename Runnable> ConditionalRange<Range, Runnable> makeConditionalRange(Range& range, Runnable&& condition) { static_assert(std::is_same<decltype(condition(*std::declval<Range>().begin())), bool>::value, "Condition must return a boolean value."); return ConditionalRange<Range, Runnable>(range, std::forward<Runnable>(condition)); } template <typename Range, typename Runnable> struct ConditionalRange { public: friend ConditionalRange makeConditionalRange<>(Range&, Runnable&&); public: using iterator_type = ConditionalIterator<decltype(std::declval<Range>().begin()), Runnable>; iterator_type begin() const { auto b = range.begin(); while ( b != range.end() && !condition(*b) ) { ++b; } return ConditionalIterator<decltype(std::declval<Range>().begin()), Runnable>(b, range.end(), condition); } iterator_type end() const { return ConditionalIterator<decltype(std::declval<Range>().begin()), Runnable>(range.end(), range.end(), condition); } private: ConditionalRange(Range& range_, Runnable&& condition_) : range(range_), condition(std::forward<Runnable>(condition_)) { } private: Range& range; Runnable condition; };
Voici la structure daide de litérateur:
template <typename Iterator, typename Runnable> struct ConditionalIterator { private: Iterator iterator; Iterator end; const Runnable& condition; public: ConditionalIterator(Iterator b, Iterator e, const Runnable& r) : iterator(b), end(e), condition(r) { } auto operator*() -> decltype(*iterator) { return *iterator; } ConditionalIterator& operator++() { do { ++iterator; } while ( iterator != end && !condition(*iterator) ); return *this; } bool operator==(const ConditionalIterator& second) { return iterator == second.iterator; } bool operator!=(const ConditionalIterator& second) { return !(*this == second); } };
Lutilisation prévue est:
std::vector<int> ns{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; for ( const auto& n : makeConditionalRange(ns, [](int a) { return a % 2 == 0; }) ) { std::cout << n << " "; } std::cout << "\n";
Démo: cliquez ici!
Quoi sont des faiblesses de mon approche? Avec quel type darguments échoue-t-il, même si cela ne devrait pas être dans le monde parfait?
Commentaires
Réponse
Vous pouvez utiliser des accolades pour revenir de vos fonctions. De cette façon, vous naurez pas à répéter des types longs et encombrants (DRY, autant que possible). Par exemple, ConditionalRange::end
:
iterator_type end() const { return { range.end(), range.end(), condition }; }
En outre, vous souhaitez probablement que votre fonction accepte également les tableaux de style C. Par conséquent, vous devez utiliser std::begin
et std::end
au lieu dappeler les fonctions membres:
iterator_type end() const { return { std::end(range), std::end(range), condition }; }
Avec cela (partout dans votre code) et quelques changements triviaux, votre code fonctionnera également pour C- tableaux de style (testés ici et cela fonctionne bien).
Nommer votre fonction
Le nom makeConditionalRange
est assez long. La fonction que vous avez créée existe déjà dans dautres bibliothèques et langages de programmation (Python me vient à lesprit) sous le nom filter
. Vous devriez considérer changer son nom pour que de nombreux utilisateurs reconnaissent le nom au premier coup dœil (de plus, il sera plus court).
for(const auto& n: iterate_if(ns, [](int a) { return a % 2 == 0; }) )
filter_iterator
?operator!=
optimise cela suroperator==