C ++ 11 범위 기반 for 루프를 좀 더 유용하게 만드는 것이 좋습니다.

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

데모 : 여기를 클릭하세요!

내용 내 접근 방식의 약점이 있습니까? 완벽한 세상에서는 안되지만 어떤 종류의 주장으로 실패합니까?

댓글

  • 약점은 보이지 않지만 " 조건부 범위를 만드는 "가 아니라 클라이언트 코드 모양에 초점을 맞춘 것으로 makeConditionalRange의 이름을 바꾸는 것이 좋습니다. 클라이언트 코드가 다음과 같이 표시되어야 함을 의미합니다. for(const auto& n: iterate_if(ns, [](int a) { return a % 2 == 0; }) )
  • 여기서 인 텐트가 Boost filter_iterator ?
  • @JerryCoffin 아니요. ' 아니요. ' Boost;-)
  • 반복자에 대한 가장 일반적인 테스트는 operator!=
  • @stefan : ' 어리석은 말을합니다. 마치 내가하지 않는다고 말하는 것과 같습니다. ' 표준 제조업체에서 자동차를 사지 않을 것입니다. 해초가 오염되지 않도록 직접 해조류를 만들어 내고 싶기 때문입니다. 문제는 라이브러리 세트가 부스트보다 훨씬 더 버그가 많을 것입니다. Boost는 수천 명의 사람들이 코드 수정을 확인 및 검증하고 올바른 관용구가 올바르게 사용되는지 확인하기 위해 피드백을 제공합니다. 읽기 : stackoverflow.com/a/149305/14065

Answer

중괄호를 사용하여 함수에서 돌아갈 수 있습니다. 이렇게하면 “길고 번거로운 유형 (DRY, 가능한 한 많이)을 반복 할 필요가 없습니다. 예 : ConditionalRange::end :

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

또한 함수에서 C 스타일 배열도 허용하기를 원할 것입니다. 따라서 std::beginstd::end 멤버 함수를 호출하는 대신 :

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

그것 (코드의 모든 부분)과 약간의 변경을 통해 코드는 C-에서도 작동합니다. 스타일 배열 ( 여기 에서 테스트했으며 제대로 작동합니다).

함수 이름 지정

이름 makeConditionalRange는 상당히 깁니다. 생성 한 함수는 이미 filter라는 이름으로 다른 라이브러리 및 프로그래밍 언어 (Python이 떠 오릅니다)에 이미 존재합니다. 많은 사용자가 이름을 한 눈에 알아볼 수 있도록 이름을 변경합니다 (또한 더 짧아짐).

답글 남기기

이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다