C ++ 11은 훌륭합니다. 아마도 가장 아름다운 기능 중 하나는 (제 생각에) 소위 범위 기반 for 루프입니다.
for ( std::size_t i(0); i < range.size(); ++i ) { // do something to range[i]; }
또는
for ( Range::iterator it(range.begin()); it != range.end(); ++it ) { // do something to *it; }
대신 또는 C ++ 11 auto로 단순화 :
for ( auto it(range.begin()); it != range.end(); ++it ) { // do something to *it; }
다음과 같이 말할 수 있습니다.
for ( auto& entry : range ) { // do something with entry. }
이것은 매우 표현 적입니다. 범위의 i ^ 번째 위치 또는 항목을 가리키는 반복자가 아니라 항목에 대해. 그러나이 구문에는 하위 범위 (예 : 마지막 항목 무시) 기능이 없습니다. 일련의 작은 도우미 구조체 / 메서드에서이 기능을 깔끔하게 추가하고 싶습니다.
내 동기를 위해 너무 많이 😉 이제 실제 거래를 위해
이 게시물에서는 range
항목에 대한 조건을 설명합니다. 기본적으로 도우미 코드는
for ( auto& entry : range ) { if ( condition(entry) ) { // do something to entry. } }
와 동일한 수준의 들여 쓰기없이 수행해야합니다.
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; };
반복자를위한 도우미 구조체입니다.
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); } };
원하는 사용법 is :
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";
데모 : 여기를 클릭하세요!
내용 내 접근 방식의 약점이 있습니까? 완벽한 세상에서는 안되지만 어떤 종류의 주장으로 실패합니까?
댓글
Answer
중괄호를 사용하여 함수에서 돌아갈 수 있습니다. 이렇게하면 “길고 번거로운 유형 (DRY, 가능한 한 많이)을 반복 할 필요가 없습니다. 예 : ConditionalRange::end
:
iterator_type end() const { return { range.end(), range.end(), condition }; }
또한 함수에서 C 스타일 배열도 허용하기를 원할 것입니다. 따라서 std::begin
및 std::end
멤버 함수를 호출하는 대신 :
iterator_type end() const { return { std::end(range), std::end(range), condition }; }
그것 (코드의 모든 부분)과 약간의 변경을 통해 코드는 C-에서도 작동합니다. 스타일 배열 ( 여기 에서 테스트했으며 제대로 작동합니다).
함수 이름 지정
이름 makeConditionalRange
는 상당히 깁니다. 생성 한 함수는 이미 filter
라는 이름으로 다른 라이브러리 및 프로그래밍 언어 (Python이 떠 오릅니다)에 이미 존재합니다. 많은 사용자가 이름을 한 눈에 알아볼 수 있도록 이름을 변경합니다 (또한 더 짧아짐).
for(const auto& n: iterate_if(ns, [](int a) { return a % 2 == 0; }) )
filter_iterator
?operator!=