A ciklusok C ++ 11 tartomány-alapúinak egy kicsit hasznosabbá tétele

A C ++ 11 nagyszerű. Valószínűleg az egyik legszebb funkció (véleményem szerint) az úgynevezett range-based-for-loop.

for ( std::size_t i(0); i < range.size(); ++i ) { // do something to range[i]; } 

vagy

for ( Range::iterator it(range.begin()); it != range.end(); ++it ) { // do something to *it; } 

helyett vagy egyszerűsítve a C ++ 11 auto-val:

for ( auto it(range.begin()); it != range.end(); ++it ) { // do something to *it; } 

ezt mondhatjuk:

for ( auto& entry : range ) { // do something with entry. } 

Ez nagyon kifejező: Beszélünk a bejegyzésről, nem pedig a tartomány i-edik pozíciójáról vagy a bejegyzésre mutató iterátorról. Ennek a szintaxisnak azonban hiányzik az altartományok képessége (például figyelmen kívül hagyva az utolsó bejegyzést). Kis segítőstruktúrák / módszerek sorában szeretném tiszta módon hozzáadni ezt a funkciót.

Ennyit a motivációmról 😉 Most az igazi üzletről.

Ebben a bejegyzésben a range bejegyzéseinek feltételeivel foglalkozom. Alapvetően a segítő kódnak meg kell felelnie a (z)

for ( auto& entry : range ) { if ( condition(entry) ) { // do something to entry. } } 

megfelelőnek, de ennek a szintnek a behúzása nélkül.

Itt van a ConditionalRange kódja:

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; }; 

Ez az iterátor segédstruktúrája:

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); } }; 

A tervezett felhasználás az:

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"; 

Bemutató: kattintson ide!

Mi van a megközelítésem gyengeségei? Milyen érvekkel nem sikerül, bár nem kellene a tökéletes világban?

Megjegyzések

  • Nem látok gyengeségeket, de úgy gondolja, hogy fontolóra kell vennie a makeConditionalRange átnevezését valamivel, amely az ügyfélkód megjelenésére összpontosít, nem pedig arra, hogy " feltételes tartományt hozzon létre ". Ez által azt jelenti, hogy az ügyfélkódnak így kell kinéznie: for(const auto& n: iterate_if(ns, [](int a) { return a % 2 == 0; }) )
  • Tehát a szándék itt jelentősen különbözik a Boost filter_iterator ?
  • @JerryCoffin Nem, nem ' t. Egyszerűen nem találok ' t nem néztem a Boostot 😉
  • Az iterátorok leggyakoribb tesztje operator!= ezt optimalizálja a operator==
  • @stefan: Hogy tompa legyek ' buta. Olyan, mintha azt mondanám, hogy nem ' t szokás autót vásárolni a szokásos gyártóktól mert én magam akarok építeni egy olyan moszatot, amely garantálja, hogy nem okoz szennyezést. A probléma az, hogy a könyvtárak készlete végtelenül hibásabb, mintsem növeli, mert csak egy szem nézi őket. A Boost több ezer ember ellenőrzi és érvényesíti a kódjavítást, és visszajelzést ad, hogy megbizonyosodjon a helyes idiómák helyes használatáról. Olvassa el: stackoverflow.com/a/149305/14065

Válasz

A zárójelek segítségével visszatérhet a funkcióiból. Így nem kell hosszú és nehézkes típusokat ismételni (amennyire csak lehetséges, SZÁRAZ). Például: ConditionalRange::end:

iterator_type end() const { return { range.end(), range.end(), condition }; } 

Valószínűleg azt is szeretné, hogy a funkciója elfogadja a C stílusú tömböket is. Ezért használja a std::begin és a std::end ahelyett, hogy meghívná a tagfüggvényeket:

iterator_type end() const { return { std::end(range), std::end(range), condition }; } 

Ezzel (mindenhol a kódban) és néhány aprócska változtatással a kódod a C- stílus tömbök ( itt tesztelve és jól működik).

A funkció elnevezése

A név makeConditionalRange elég hosszú. Az Ön által létrehozott függvény már létezik más könyvtárakban és programozási nyelvekben (a Python jut eszembe) filter néven. Fontolja meg a név megváltoztatása annak érdekében, hogy sok felhasználó első pillantásra felismerje a nevet (ráadásul rövidebb lesz).

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük