C ++ 11 on hienoa. Todennäköisesti yksi kauneimmista piirteistä (mielestäni) on niin kutsuttu alue-pohjainen silmukalle.
for ( std::size_t i(0); i < range.size(); ++i ) { // do something to range[i]; }
tai
for ( Range::iterator it(range.begin()); it != range.end(); ++it ) { // do something to *it; }
sijaan yksinkertaistettuna automaattisella C ++ 11: llä:
for ( auto it(range.begin()); it != range.end(); ++it ) { // do something to *it; }
voimme sanoa tämän:
for ( auto& entry : range ) { // do something with entry. }
Tämä on hyvin ilmeikäs: Puhumme tietoja merkinnästä, ei alueen i ^ sijasta tai merkintää osoittavasta iteraattorista. Tästä syntaksista puuttuu kuitenkin kyky saada alialoja (esim. Jättää huomiotta viimeinen merkintä). Haluan lisätä tämän toiminnon puhtaalla tavalla sarjaan pieniä auttajarakenteita / -menetelmiä.
Paljon motivaatioistani 😉 Nyt, oikeasta kaupasta.
Tässä viestissä käsittelen ehtoja range
-merkinnöille. Periaatteessa auttajakoodin tulisi suorittaa vastaava kuin
for ( auto& entry : range ) { if ( condition(entry) ) { // do something to entry. } }
, mutta ilman kyseistä sisennystasoa.
Tässä on ConditionalRange-koodi:
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; };
Tämä on iteraattorin apurakenne:
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); } };
Tarkoitettu käyttö on:
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";
Esittely: napsauta tätä!
Mitä ovatko lähestymistapani heikkouksia? Millaisilla argumenteilla se epäonnistuu, vaikka sen ei pitäisi olla täydellisessä maailmassa?
Kommentit
vastaus
Voit palata toiminnoistasi aaltosulkeilla. Tällä tavalla sinun ei tarvitse toistaa pitkiä ja hankalia tyyppejä (DRY, niin paljon kuin mahdollista). Esimerkiksi ConditionalRange::end
:
iterator_type end() const { return { range.end(), range.end(), condition }; }
Haluat todennäköisesti myös, että toiminto hyväksyy myös C-tyyliset taulukot. Siksi sinun tulisi käyttää std::begin
ja std::end
jäsenfunktioiden kutsumisen sijaan:
iterator_type end() const { return { std::end(range), std::end(range), condition }; }
Tämän avulla (kaikkialla koodissasi) ja joitain vähäpätöisiä muutoksia koodisi toimii myös C- tyylitaulukot (testattu täällä ja se toimii hyvin).
Funktion nimeäminen
Nimi makeConditionalRange
on melko pitkä. Luomasi toiminto on jo olemassa muissa kirjastoissa ja ohjelmointikielissä (Python tulee mieleeni) nimellä filter
. Harkitse nimen muuttaminen, jotta monet käyttäjät tunnistavat nimen ensi silmäyksellä (lisäksi se on lyhyempi).
for(const auto& n: iterate_if(ns, [](int a) { return a % 2 == 0; }) )
filter_iterator
?operator!=
optimoi tämän ylioperator==