C ++로 내부에 "if"조건이있는 "for"루프를 피하려면 어떻게해야합니까?


111

내가 작성하는 거의 모든 코드를 사용하여 궁극적으로 컬렉션 내부에 순진한 "if"조건으로 끝나는 집합 감소 문제를 종종 다루고 있습니다. 다음은 간단한 예입니다.

for(int i=0; i<myCollection.size(); i++)
{
     if (myCollection[i] == SOMETHING)
     {
           DoStuff();
     }
}

함수형 언어를 사용하면 컬렉션을 다른 컬렉션으로 (쉽게) 축소하여 문제를 해결 한 다음 축소 된 집합에서 모든 작업을 수행 할 수 있습니다. 의사 코드에서 :

newCollection <- myCollection where <x=true
map DoStuff newCollection

그리고 C #과 같은 다른 C 변형에서는 다음과 같은 where 절을 사용하여 줄일 수 있습니다.

foreach (var x in myCollection.Where(c=> c == SOMETHING)) 
{
   DoStuff();
}

또는 더 나은 (적어도 내 눈에는)

myCollection.Where(c=>c == Something).ToList().ForEach(d=> DoStuff(d));

물론 저는 많은 패러다임 믹싱과 주관적 / 의견 기반 스타일을하고 있지만 C ++에서이 선호하는 기술을 사용할 수있는 근본적인 무언가를 놓치고 있다는 느낌을받을 수밖에 없습니다. 누군가 나를 깨달을 수 있습니까?


7
C ++ 표준 라이브러리의 기능 중, 당신은 시도 할 수 std::copy_if있지만 선택은 게으른 없습니다
milleniumbug

14
range-v3에 관심이있을 수 있습니다 . 또한 C ++에 TS로 제공되고 향후 릴리스에서 표준화 될 것입니다.
NathanOliver

12
나는 당신이 언급 한 if내부가 for다른 예와 거의 기능적으로 동등 할뿐만 아니라 많은 경우에 더 빠를 것이라는 점을 지적 할 필요가 있다고 느낍니다 . 또한 기능적 스타일을 좋아한다고 주장하는 사람에게, 당신이 홍보하는 것은 DoStuff분명히 부 효과가 있기 때문에 함수형 프로그래밍의 사랑받는 순수성 개념에 위배되는 것 같습니다.
Pharap

60
사람들이 모든 논리를 한 줄 에 결합하는 링크를 사용하면 어떻게 든 더보기 좋거나 읽기 쉽게 만드는 이유를 이해하지 못했습니다 . 귀하의 C ++ 코드 조각 바로 상단은 지금까지 당신의 모든 가능성 밖으로 나에게 가장 읽을. 그리고 효율성은 변하지 않을 것이기 때문에 삭제 한 코드 줄 수에 따라 비용을 지불받지 않는 한 작성하지 않는 이유를 이해할 수 없습니다.
Cody Gray

10
@CodyGray 동의 : 그것은 단지 구문상의 설탕입니다. 질문 제목은 분기를 피하고 추상화하에 숨기는 것이 매우 다르기 때문에 오해의 소지가 있습니다.
edmz

답변:


99

IMHO 내부에 if가있는 for 루프를 사용하는 것이 더 간단하고 읽기 쉽습니다. 그러나 이것이 귀찮다면 for_each_if아래와 같은 것을 사용할 수 있습니다 .

template<typename Iter, typename Pred, typename Op> 
void for_each_if(Iter first, Iter last, Pred p, Op op) {
  while(first != last) {
    if (p(*first)) op(*first);
    ++first;
  }
}

사용 사례 :

std::vector<int> v {10, 2, 10, 3};
for_each_if(v.begin(), v.end(), [](int i){ return i > 5; }, [](int &i){ ++i; });

라이브 데모


10
그것은 매우 영리합니다. 나는 또한 그것이 간단하지 않다는 데 동의 할 것이며 다른 사람들이 소비하는 C ++를 프로그래밍 할 때 아마도 if 조건을 사용할 것입니다. 그러나 그것은 내가 개인적으로 사용하기 위해 필요한 것입니다! :)
Darkenor

14
@Default 컨테이너보다는 반복자 쌍을 전달하는 것이 더 유연하고 관용적 인 C ++입니다.
Mark B

8
@Slava, 일반적으로 범위는 알고리즘 수를 줄이지 않습니다. 예를 들어, 여전히 필요 find_if하고 find범위 또는 반복기 쌍에서 작동하는지 여부. (같은 몇 가지 예외가 있습니다 for_eachfor_each_n). 재채기 할 때마다 새 알고리즘을 작성하는 것을 피하는 방법은 기존 알고리즘과 함께 다른 작업을 사용하는 것입니다 . 예를 들어에 for_each_if전달 된 콜 러블에 조건을 포함하는 대신 for_each예를 들어for_each(first, last, [&](auto& x) { if (cond(x)) f(x); });
Jonathan Wakely

9
첫 번째 문장에 동의해야합니다. 표준 for-if 솔루션은 훨씬 더 읽기 쉽고 작업하기 쉽습니다. 람다 구문과 단순한 루프를 처리하기 위해 다른 곳에서 정의 된 템플릿을 사용하면 다른 개발자를 짜증나게하거나 혼동 할 수 있다고 생각합니다. 당신은 지역 성과 성능을 희생하고 있습니다 ... 무엇? 한 줄에 무언가를 쓸 수 있습니까?
user1354557

45
기침 @Darkenor, 일반적으로 " 매우 영리한"프로그래밍 미래의 자신을 포함하여 다른 모든 사람을 괴롭히기 때문에 피해야 합니다.
라이언

48

Boost는 범위 기반으로 사용할 수있는 범위를 제공합니다. 범위들은 내부 데이터 구조를 복사하지 않도록, 그들은 단지 '도'를 제공하는 이점을 갖는다 (즉, begin(), end()범위 및 operator++(), operator==()반복자 용). 관심이있을 수 있습니다. http://www.boost.org/libs/range/doc/html/range/reference/adaptors/reference/filtered.html

#include <boost/range/adaptor/filtered.hpp>
#include <iostream>
#include <vector>

struct is_even
{
    bool operator()( int x ) const { return x % 2 == 0; }
};

int main(int argc, const char* argv[])
{
    using namespace boost::adaptors;

    std::vector<int> myCollection{1,2,3,4,5,6,7,8,9};

    for( int i: myCollection | filtered( is_even() ) )
    {
        std::cout << i;
    }
}

1
대신 OPs 예제를 사용하는 것이 좋습니다 (예 : is_even=> condition, input=> myCollection등).
기본값

이것은 매우 훌륭한 대답이며 확실히 제가하고자하는 것입니다. 누군가가 지연 / 지연 실행을 사용하는 표준 준수 방법을 생각 해낼 수 없다면 수락을 보류 할 것입니다. 찬성.
Darkenor

5
@Darkenor : Boost가 문제인 경우 (예 : 회사 정책 및 관리자의 지혜로 인해 사용이 금지 된 경우) 간단히 정의 filtered()할 수 있습니다. 즉, 사용하는 것이 좋습니다. 일부 임시 코드보다 지원되는 lib.
lorro

당신의 의견에 완전히 찬성합니다. 질문이 부스트 라이브러리가 아닌 C ++ 자체에 맞춰 졌기 때문에 표준 준수 방식이 먼저 왔기 때문에 수락했습니다. 그러나 이것은 정말 훌륭합니다. 또한 - 그래, 난 슬프게 ... 터무니없는 이유로 부스트를 금지 많은-A-장소에서 일한
Darkenor

@LeeClagett :? .
lorro

44

새 알고리즘을 만드는 대신 수락 된 답변처럼 조건을 적용하는 함수가있는 기존 알고리즘을 사용할 수 있습니다.

std::for_each(first, last, [](auto&& x){ if (cond(x)) { ... } });

또는 정말로 새로운 알고리즘을 원한다면 for_each반복 논리를 복제하는 대신 적어도 거기 에서 재사용 하십시오.

template<typename Iter, typename Pred, typename Op> 
  void
  for_each_if(Iter first, Iter last, Pred p, Op op) {
    std::for_each(first, last, [&](auto& x) { if (p(x)) op(x); });
  }

표준 라이브러리를 사용하는 것이 훨씬 더 좋고 명확합니다.
익명

4
std::for-each(first, last, [&](auto& x) {if (p(x)) op(x); });보다 완전히 간단 하기 때문에 for (Iter x = first; x != last; x++) if (p(x)) op(x);}?
user253751 jul.

2
표준 라이브러리를 재사용하는 @immibis는 반복기 유효성 검사 또는 (C ++ 17에서) 인수를 하나 더 추가하는 것만으로도 훨씬 더 쉽게 병렬화 std::for_each(std::execution::par, first, last, ...);할 수 있습니다. 이러한 것들을 손으로 쓴 루프에 추가하는 것이 얼마나 쉬울까요?
조나단 Wakely

1
의 #pragma OMP 평행을위한
마크 K 코완

2
@mark 죄송합니다. 소스 코드 또는 빌드 체인의 임의의 특성 때문에 성가 시게 깨지기 쉬운 병렬 비표준 컴파일러 확장으로 인해 진단없이 성능 향상이 전혀 발생하지 않습니다.
Yakk-Adam Nevraumont

21

피하는 아이디어

for(...)
    if(...)

반 패턴으로서의 구조가 너무 넓습니다.

루프 내에서 특정 표현식과 일치하는 여러 항목을 처리하는 것은 완전히 괜찮으며 코드가 그보다 훨씬 명확해질 수 없습니다. 처리가 화면에 맞추기에는 너무 커지면 서브 루틴을 사용하는 좋은 이유가되지만 여전히 조건문은 루프 내부에 배치하는 것이 가장 좋습니다.

for(...)
    if(...)
        do_process(...);

훨씬 더 선호됩니다

for(...)
    maybe_process(...);

하나의 요소 만 일치 할 때 반 패턴이됩니다. 그러면 먼저 요소를 검색하고 루프 외부에서 처리를 수행하는 것이 더 명확하기 때문입니다.

for(int i = 0; i < size; ++i)
    if(i == 5)

이것의 극단적이고 명백한 예입니다. 더 미묘하고 더 일반적인 것은 다음과 같은 공장 패턴입니다.

for(creator &c : creators)
    if(c.name == requested_name)
    {
        unique_ptr<object> obj = c.create_object();
        obj.owner = this;
        return std::move(obj);
    }

본문 코드가 한 번만 실행된다는 것이 분명하지 않기 때문에 읽기 어렵습니다. 이 경우 조회를 분리하는 것이 좋습니다.

creator &lookup(string const &requested_name)
{
    for(creator &c : creators)
        if(c.name == requested_name)
            return c;
}

creator &c = lookup(requested_name);
unique_ptr obj = c.create_object();

여전히 if안에가 for있지만 컨텍스트에서 그것이 무엇을하는지 명확 해집니다. 조회가 변경되지 않는 한 (예 : a로 map) 이 코드를 변경할 필요가 없으며 , create_object()한 번만 호출 되는 것이 바로 루프 내부가 아닙니다.


나는 이것이 어떤 의미에서 제기 된 질문에 대답을 거부하더라도 사려 깊고 균형 잡힌 개요로 좋아합니다. 나는 것을 발견 for( range ){ if( condition ){ action } }스타일은 쉽게 한 번에 일을 한 덩어리를 읽을 수 있습니다 만 기본적인 언어 구조에 대한 지식을 사용합니다.
PJTraill

@PJTraill, 질문이 표현 된 방식은 Raymond Chen 이화물을 컬트하고 어떻게 든 절대적이 된 for-if antipattern에 대한 항의를 떠올리게했습니다. 나는 그것이 for(...) if(...) { ... }종종 최선의 선택 이라는 데 전적으로 동의합니다 (그래서 내가 작업을 서브 루틴으로 분할하라는 권장 사항을 자격이 부여 된 이유입니다).
Simon Richter

1
나를 위해 일을 명확히 해준 링크에 감사드립니다. " for-if " 라는 이름 은 오해의 소지가 있으며 " for-all-if-one "또는 " lookup-avoidance " 와 같은 형식이어야합니다 . 그것은 2005 년Wikipedia에서 Abstraction inversion 이 " 복잡한 (하나) 위에 간단한 구조를 만들었을 때"라고 설명 했던 방식을 생각 나게합니다.- 내가 그것을 다시 쓸 때까지! 실제로 조회가 발생한 유일한 장소 인 경우 조회 프로세스 종료 형식을 고치려고 서두르지도 않을 것 입니다. for(…)if(…)…
PJTraill

17

여기에 비교적 최소한의 빠른 filter기능이 있습니다.

술어가 필요합니다. iterable을 취하는 함수 객체를 반환합니다.

for(:)루프 에서 사용할 수있는 이터 러블을 반환합니다 .

template<class It>
struct range_t {
  It b, e;
  It begin() const { return b; }
  It end() const { return e; }
  bool empty() const { return begin()==end(); }
};
template<class It>
range_t<It> range( It b, It e ) { return {std::move(b), std::move(e)}; }

template<class It, class F>
struct filter_helper:range_t<It> {
  F f;
  void advance() {
    while(true) {
      (range_t<It>&)*this = range( std::next(this->begin()), this->end() );
      if (this->empty())
        return;
      if (f(*this->begin()))
        return;
    }
  }
  filter_helper(range_t<It> r, F fin):
    range_t<It>(r), f(std::move(fin))
  {
      while(true)
      {
          if (this->empty()) return;
          if (f(*this->begin())) return;
          (range_t<It>&)*this = range( std::next(this->begin()), this->end() );
      }
  }
};

template<class It, class F>
struct filter_psuedo_iterator {
  using iterator_category=std::input_iterator_tag;
  filter_helper<It, F>* helper = nullptr;
  bool m_is_end = true;
  bool is_end() const {
    return m_is_end || !helper || helper->empty();
  }

  void operator++() {
    helper->advance();
  }
  typename std::iterator_traits<It>::reference
  operator*() const {
    return *(helper->begin());
  }
  It base() const {
      if (!helper) return {};
      if (is_end()) return helper->end();
      return helper->begin();
  }
  friend bool operator==(filter_psuedo_iterator const& lhs, filter_psuedo_iterator const& rhs) {
    if (lhs.is_end() && rhs.is_end()) return true;
    if (lhs.is_end() || rhs.is_end()) return false;
    return lhs.helper->begin() == rhs.helper->begin();
  }
  friend bool operator!=(filter_psuedo_iterator const& lhs, filter_psuedo_iterator const& rhs) {
    return !(lhs==rhs);
  }
};
template<class It, class F>
struct filter_range:
  private filter_helper<It, F>,
  range_t<filter_psuedo_iterator<It, F>>
{
  using helper=filter_helper<It, F>;
  using range=range_t<filter_psuedo_iterator<It, F>>;

  using range::begin; using range::end; using range::empty;

  filter_range( range_t<It> r, F f ):
    helper{{r}, std::forward<F>(f)},
    range{ {this, false}, {this, true} }
  {}
};

template<class F>
auto filter( F&& f ) {
    return [f=std::forward<F>(f)](auto&& r)
    {
        using std::begin; using std::end;
        using iterator = decltype(begin(r));
        return filter_range<iterator, std::decay_t<decltype(f)>>{
            range(begin(r), end(r)), f
        };
    };
};

나는 지름길을 택했다. 실제 라이브러리는 for(:)내가 한 자격을 갖춘 의사 파사드가 아닌 실제 반복자를 만들어야합니다 .

사용 시점에서 다음과 같이 보입니다.

int main()
{
  std::vector<int> test = {1,2,3,4,5};
  for( auto i: filter([](auto x){return x%2;})( test ) )
    std::cout << i << '\n';
}

꽤 멋지고

1
3
5

라이브 예 .

이런 종류의 작업을 수행하는 Rangesv3라는 C ++에 제안 된 추가 기능이 있습니다. boost또한 필터 범위 / 반복자를 사용할 수 있습니다. boost에는 위의 글을 훨씬 더 짧게 만드는 도우미도 있습니다.


15

언급 할만큼 충분히 사용되었지만 아직 언급되지 않은 스타일은 다음과 같습니다.

for(int i=0; i<myCollection.size(); i++) {
  if (myCollection[i] != SOMETHING)
    continue;

  DoStuff();
}

장점 :

  • DoStuff();조건 복잡성이 증가 할 때 들여 쓰기 수준을 변경하지 않습니다 . 논리적으로 루프 DoStuff();의 최상위 수준에 있어야합니다 for.
  • 바로이 클리어하게 그 위에 루프 반복 SOMETHING컬렉션 S, 폐쇄 후 전혀 없다는 것을 확인하기 위해, 리더 없이도 }if블록.
  • 라이브러리 나 도우미 매크로 또는 함수가 필요하지 않습니다.

단점 :

  • continue다른 흐름 제어 문처럼, 너무 많은 사람들이 반대되는 어려운 추적 코드로 이어질 방법으로 오용됩니다 어떤 이들의 사용 :을 피가 몇 가지 따르는 것이 코딩의 유효한 스타일이 continue, 방지 break이외의 에서 함수의 끝이 아닌 다른 switch것을 피 return합니다.

3
나는 for여러 줄로 실행 되는 루프에서 두 줄 "아니라면 계속"이 훨씬 더 명확하고 논리적이며 읽기 쉽다고 주장합니다. for명령문이 잘 읽힌 후 즉시 "이 경우 건너 뛰기" 라고 말하면 루프의 나머지 기능 측면을 들여 쓰지 않습니다. (가)하면 continue더 다운 그러나, 일부 선명도 (일부 작업이 항상 전에 수행됩니다 즉, 경우에 희생 if문).
익명

11
for(auto const &x: myCollection) if(x == something) doStuff();

for나에게 C ++ 관련 이해 와 거의 비슷해 보입니다 . 당신에게?


나는 자동 키워드가 C ++ 11 이전에 존재했다고 생각하지 않으므로 매우 고전적인 C ++라고 말하지 않을 것입니다. 여기 주석에서 질문을 할 수 있다면 "auto const"는 컴파일러에게 원하는대로 모든 요소를 ​​재정렬 할 수 있다고 알려줄까요? 그렇다면 컴파일러가 분기를 피하는 것이 더 쉬울 것입니다.
mathreadler

1
@mathreadler 사람들이 "고전적인 C ++"에 대한 걱정을 빨리 멈출수록 좋습니다. C ++ 11은 언어에 대한 대 진화적인 사건이었고 5 년이 되었습니다. 우리가 추구 하는 최소한의 것이어야합니다 . 어쨌든, OP는 그것과 C ++ 14에 태그를 붙였습니다 (더 좋습니다!). 아니오, auto const반복 순서에 아무런 영향이 없습니다. ranged-based를 조회 for하면 기본적으로 암시 적 역 참조 begin()end()사용 하여 표준 루프를 수행한다는 것을 알 수 있습니다 . 반복되는 컨테이너의 주문 보장 (있는 경우)을 위반할 수있는 방법은 없습니다. 그것은 지구의 얼굴 떨어져 웃었다했습니다 것
underscore_d

1
@mathreadler, 실제로 그것은 아주 다른 의미를 가졌습니다. 존재하지 않는 것은 range-for ... 및 기타 다른 C ++ 11 기능입니다. 여기서 제가 의미하는 것은 range-fors, std::futures, std::functions, 심지어 이러한 익명 클로저도 구문에서 C ++ 방식이 매우 좋다는 것입니다. 모든 언어는 고유 한 용어를 가지고 있으며 새로운 기능을 통합 할 때 이전의 잘 알려진 구문을 모방하려고합니다.
bipll

@underscore_d, as-if 규칙을 준수하는 경우 컴파일러는 모든 변환을 수행 할 수 있습니다.
bipll

1
흠, 그게 무슨 뜻일까요?
bipll

7

DoStuff ()가 미래에 어떻게 든 i에 의존한다면 나는이 보장 된 분기없는 비트 마스킹 변형을 제안 할 것입니다.

unsigned int times = 0;
const int kSize = sizeof(unsigned int)*8;
for(int i = 0; i < myCollection.size()/kSize; i++){
  unsigned int mask = 0;
  for (int j = 0; j<kSize; j++){
    mask |= (myCollection[i*kSize+j]==SOMETHING) << j;
  }
  times+=popcount(mask);
}

for(int i=0;i<times;i++)
   DoStuff();

여기서 popcount는 모집단 카운트를 수행하는 함수입니다 (카운트 비트 수 = 1). i와 그 이웃들에게보다 진보 된 제약을 둘 수있는 자유가있을 것입니다. 필요하지 않은 경우 내부 루프를 제거하고 외부 루프를 다시 만들 수 있습니다.

for(int i = 0; i < myCollection.size(); i++)
  times += (myCollection[i]==SOMETHING);

다음에

for(int i=0;i<times;i++)
   DoStuff();

6

또한 컬렉션을 재정렬하는 데 신경 쓰지 않는다면 std :: partition이 저렴합니다.

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

void DoStuff(int i)
{
    std::cout << i << '\n';
}

int main()
{
    using namespace std::placeholders;

    std::vector<int> v {1, 2, 5, 0, 9, 5, 5};
    const int SOMETHING = 5;

    std::for_each(v.begin(),
                  std::partition(v.begin(), v.end(),
                                 std::bind(std::equal_to<int> {}, _1, SOMETHING)), // some condition
                  DoStuff); // action
}

그러나 std::partition컨테이너를 재정렬합니다.
celtschk

5

나는 위의 솔루션의 복잡성에 경외감을 느낍니다. 간단한 제안을하려고 #define foreach(a,b,c,d) for(a; b; c)if(d)했지만 몇 가지 명백한 결점이 있습니다. 예를 들어 루프에서 세미콜론 대신 쉼표를 사용해야하며 a또는 에서 쉼표 연산자를 사용할 수 없습니다 c.

#include <list>
#include <iostream>

using namespace std; 

#define foreach(a,b,c,d) for(a; b; c)if(d)

int main(){
  list<int> a;

  for(int i=0; i<10; i++)
    a.push_back(i);

  for(auto i=a.begin(); i!=a.end(); i++)
    if((*i)&1)
      cout << *i << ' ';
  cout << endl;

  foreach(auto i=a.begin(), i!=a.end(), i++, (*i)&1)
    cout << *i << ' ';
  cout << endl;

  return 0;
}

3
일부 답변의 복잡성은 처음에는 재사용 가능한 제네릭 메서드 (한 번만 수행)를 보여준 다음 사용하기 때문에 높기 때문입니다. 전체 애플리케이션에 if 조건이있는 루프 가 하나 있으면 효과적이지 않지만 천 번 발생하면 매우 효과적입니다.
gnasher729

1
대부분의 제안과 마찬가지로 이렇게하면 범위와 선택 조건을 식별하는 것이 더 어렵고 쉽지 않습니다. 그리고 매크로를 사용하면 여기에 놀라운 일이 없더라도식이 평가되는시기 (및 빈도)에 대한 불확실성이 증가합니다.
PJTraill

2

i : s가 중요한 경우에 대한 또 다른 솔루션입니다. 이것은 doStuff ()를 호출 할 인덱스를 채우는 목록을 작성합니다. 다시 한 번 중요한 점은 분기를 피하고 파이프 라인 가능한 산술 비용으로 거래하는 것입니다.

int buffer[someSafeSize];
int cnt = 0; // counter to keep track where we are in list.
for( int i = 0; i < container.size(); i++ ){
   int lDecision = (container[i] == SOMETHING);
   buffer[cnt] = lDecision*i + (1-lDecision)*buffer[cnt];
   cnt += lDecision;
}

for( int i=0; i<cnt; i++ )
   doStuff(buffer[i]); // now we could pass the index or a pointer as an argument.

"마법의"라인은 값을 유지하고 제자리에 머 무르거나 위치를 세고 값을 추가하기 위해 산술적으로 계산하는 버퍼 로딩 라인입니다. 따라서 우리는 일부 논리 및 산술과 캐시 적중에 대한 잠재적 분기를 교환합니다. 이것이 유용한 일반적인 시나리오는 doStuff ()가 소량의 파이프 라인 가능한 계산을 수행하고 호출 사이의 분기가 해당 파이프 라인을 중단 할 수있는 경우입니다.

그런 다음 버퍼를 반복하고 cnt에 도달 할 때까지 doStuff ()를 실행합니다. 이번에는 필요한 경우 doStuff () 호출에 사용할 수 있도록 버퍼에 현재 i를 저장합니다.


1

코드 패턴을 범위의 하위 집합에 일부 기능을 적용하는 것으로 설명 할 수 있습니다. 즉, 전체 범위에 필터를 적용한 결과에 적용하는 것입니다.

이것은 Eric Neibler의 ranges-v3 라이브러리 를 사용하여 가장 간단한 방식으로 달성 할 수 있습니다 . 인덱스로 작업하고 싶기 때문에 약간 눈에 띄지 만

using namespace ranges;
auto mycollection_has_something = 
    [&](std::size_t i) { return myCollection[i] == SOMETHING };
auto filtered_view = 
    views::iota(std::size_t{0}, myCollection.size()) | 
    views::filter(mycollection_has_something);
for (auto i : filtered_view) { DoStuff(); }

하지만 지수를 포기하고 싶다면 다음과 같은 결과를 얻을 수 있습니다.

auto is_something = [&SOMETHING](const decltype(SOMETHING)& x) { return x == SOMETHING };
auto filtered_collection = myCollection | views::filter(is_something);
for (const auto& x : filtered_collection) { DoStuff(); }

더 좋은 IMHO입니다.

추신-범위 라이브러리는 대부분 C ++ 20의 C ++ 표준에 포함됩니다.


0

Mike Acton에 대해 언급하겠습니다. 그는 분명히 다음과 같이 말할 것입니다.

그렇게해야한다면 데이터에 문제가있는 것입니다. 데이터를 정렬하십시오!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.