for 루프의 std :: for_each 장점


166

std::for_each오버 for루프의 장점이 있습니까? 나에게 std::for_each코드의 가독성을 방해하는 것 같습니다. 그렇다면 일부 코딩 표준에서 사용을 권장하는 이유는 무엇입니까?


10
std::for_each가독성 과 함께 사용 boost.lambda하거나 boost.bind가독성을 향상시킬 수있는 경우

질문 및 허용 된 답변은 2010 년입니다. 2018 년 이후의 최신 답변은 다음을 참조하십시오. fluentcpp.com/2018/03/30/is-stdfor_each-obsolete
Erel Segal-Halevi

답변:


181

C ++ 11 (이전의 C ++ 0x) 의 좋은 점 은이 지루한 논쟁이 해결 될 것입니다.

전체 컬렉션을 반복하고 싶어하는 올바른 마음을 가진 사람은 여전히 ​​이것을 사용하지 않습니다.

for(auto it = collection.begin(); it != collection.end() ; ++it)
{
   foo(*it);
}

아니면 이거

for_each(collection.begin(), collection.end(), [](Element& e)
{
   foo(e);
});

영역 기반 for루프 구문을 사용할 수있다 :

for(Element& e : collection)
{
   foo(e);
}

이러한 종류의 구문은 Java 및 C #에서 한동안 사용 가능했으며 실제로는 최근에 본 모든 Java 또는 C # 코드에서 foreach기존 for루프 보다 더 많은 루프 가 있습니다.


12
실제로, 특종을 가진 foreach 루프는 오랫동안 부스트되어 왔으며 여전히 for_each 및 람다 함수로 반복하고 싶습니다.
Viktor Sehr

19
전체 컨테이너 범위를 원한다는 가정은 문제의 일부가 아니므로 이것은 부분적인 대답 일뿐입니다.
Adrian McCarthy

6
요소를 반복하는 것이 원하는 유일한 방법은 아니므로 find / partition / copy_replace_if 및 기타에 대해 배우기 위해 for_each를 사용하는 것이 좋습니다. 하다.
Macke

10
Range-for는 실제로 반복자가 필요할 때를 제외하고는 훌륭합니다 (그러면 도달 할 방법이 없습니다).
데이먼

5
난 사용하지 않는 게 좋을 Element & e대로 auto & e(또는 auto const &e) 더 나은 보인다. 내가 사용하는 거라고 Element const e내가 암시 적 변환을 할 때, 소스가 다른 종류의 모음입니다 말할 때, 나는 그들로 변환 할 (관계없이) Element.
Nawaz

53

몇 가지 이유는 다음과 같습니다.

  1. 익숙하지 않거나 읽기 쉬운 도구를 사용하지 않아서 가독성을 방해하는 것 같습니다. (헬퍼의 경우 boost :: range 및 boost :: bind / boost :: lambda를 참조하십시오. 이들 중 다수는 C ++ 0x로 이동하여 for_each 및 관련 함수를보다 유용하게 만듭니다.)

  2. 모든 반복자와 작동하는 for_each 위에 알고리즘을 작성할 수 있습니다.

  3. 어리석은 타이핑 버그의 가능성을 줄입니다.

  4. 또한, 같은 STL-알고리즘의 나머지에 당신의 마음을 열고 find_if, sort, replace, 등 이들은 더 이상 이상한 보이지 않는 것입니다. 이것은 큰 승리가 될 수 있습니다.

업데이트 1 :

가장 중요한 것은, 그것이 for_each존재하는 모든 것과 같은 for-loops를 넘어서서 find / sort / partition / copy_replace_if, parallel execution .. 또는 무엇이든과 같은 다른 STL- 알고리즘을 살펴 보는 것입니다.

for_each의 형제의 "나머지"를 사용하여 많은 처리를 매우 간결하게 작성할 수 있지만 다양한 내부 논리를 사용하여 for-loop를 작성하는 것만으로도 그 사용법을 배우지 못할 것입니다. 바퀴를 반복해서 발명하게됩니다.

(곧 사용 가능한 범위 스타일 for_each) :

for_each(monsters, boost::mem_fn(&Monster::think));

또는 C ++ x11 람다 :

for_each(monsters, [](Monster& m) { m.think(); });

IMO가 다음보다 더 읽기 쉽습니다.

for(Monsters::iterator i = monsters.begin(); i != monsters.end(); ++i) {
    i->think();
} 

또한 이것 (또는 람다와 함께, 다른 사람들을보십시오) :

for_each(bananas, boost::bind(&Monkey::eat, my_monkey, _1));

다음보다 더 간결합니다.

for(Bananas::iterator i = bananas.begin(); i != bananas.end(); ++i) {
    my_monkey->eat(*i);
} 

특히 여러 함수를 순서대로 호출하는 경우 ...하지만 아마도 나뿐입니다. ;)

업데이트 2 : 반복자 쌍 대신 범위와 함께 작동하는 자체 stl-algos 래퍼를 작성했습니다. boost :: range_ex가 출시되면 C ++ 0x에도 포함됩니까?


+1, 여러 함수 또는 중첩 유형 : outer_class::inner_class::iterator또는 템플릿 인수 : typename std::vector<T>::iterator... for 구문 자체가 자체적으로 많은 행 구문으로 실행될 수 있습니다.
David Rodríguez-dribeas

4
(btw : for_each두 번째 예에서 잘못된 것임for_each( bananas.begin(), bananas.end(),...
David Rodríguez-dribeas

두 개의 반복자 대신 범위를 사용하는 래퍼를 작성했습니다. 이것들은 나중에 사용할 수 있지만 (range_ex 참조) 어쨌든 누구나 가지고 있어야합니다. (
그에

1
병렬 처리 지원은 없습니다. 여기에 한 가지 이유가 있습니다. 이기종 병렬 컴퓨팅에 cuda / gpu를 사용하도록 구현을 추가 할 수 있습니다.
shuva

24

for_each더 일반적입니다. 시작 / 종료 반복자를 전달하여 모든 유형의 컨테이너를 반복하는 데 사용할 수 있습니다. for_each반복 코드를 업데이트하지 않고도 사용하는 함수 아래에서 컨테이너를 교체 할 수 있습니다 . 세상에는 다른 std::vector오래된 컨테이너 와 일반 C 어레이의 이점을 보려면 다른 컨테이너가 있다는 것을 고려해야합니다 for_each.

가장 큰 단점은 for_eachfunctor가 필요하기 때문에 구문이 복잡하다는 것입니다. 이것은 람다를 도입하여 C ++ 11 (이전 C ++ 0x)에서 수정되었습니다.

std::vector<int> container;
...
std::for_each(container.begin(), container.end(), [](int& i){
    i+= 10;
});

3 년 후에는 이상하게 보이지 않을 것입니다.


3
+1. for_each가 두 개의 반복자가 아닌 컨테이너 / 범위를 취할 때의 광고는 훨씬 더 훌륭합니다.
Macke

2
@Marcus : 구조물에 대한-원거리 및 구문 자체가 '의 for_each'을 읽지 않습니다이 될 것이다 : for ( int v : int_vector ) {(이 BOOST_FOREACH 오늘 시뮬레이션 할 수 있습니다 경우에도)
dribeas - 데이비드 로드리게스

@David : 범위 기반 함수의 일반적인 추가 (따라서 이러한 for_each, copy, remove_if 등의 함수를 사용하여 범위를 사용할 수 있음)를 참조하고 있습니다.
Macke

1
다음과 같이 쓸 수없는 이유 : std::for_each(container, [](int& i){ ... });. 왜 컨테이너를 두 번 쓰도록 강요합니까?
Giorgio

1
@freitass : 이전 주석에서와 같이 컨테이너를 한 번 작성하면 명시 적으로 호출하지 않고 begin end 반복자를 사용하는 것이 기본값이 될 수 있습니다. 컬렉션 (Ruby, Scala 등)에서 고차 함수를 제공하는 대부분의 언어는 container.each { ... }시작 및 끝 반복자를 언급하지 않고 같은 것을 작성 합니다. 끝 반복기를 항상 지정 해야하는 것이 조금 중복됩니다.
Giorgio

17

개인적으로, 사용 방법을 std::for_each벗어나야 할 때마다 (특수 기능 펑터 작성 / 복잡한 작성 boost::lambda) BOOST_FOREACHC ++ 0x의 범위 기반을 사용하여 더 명확하게 알 수 있습니다.

BOOST_FOREACH(Monster* m, monsters) {
     if (m->has_plan()) 
         m->act();
}

vs

std::for_each(monsters.begin(), monsters.end(), 
  if_then(bind(&Monster::has_plan, _1), 
    bind(&Monster::act, _1)));

11

매우 주관적이며 일부는 동일한 규칙으로 다른 컬렉션을 처리 할 수 ​​있기 때문에 for_each 코드를 읽기 쉽게 만들 것이라고 말합니다 . for_eachitslef는 루프로 구현됩니다

template<class InputIterator, class Function>
  Function for_each(InputIterator first, InputIterator last, Function f)
  {
    for ( ; first!=last; ++first ) f(*first);
    return f;
  }

그래서 당신에게 맞는 것을 선택하는 것은 당신에게 달려 있습니다.


11

많은 알고리즘 함수와 마찬가지로 초기 반응은 루프보다 foreach를 사용하는 것이 더 읽기 어렵다고 생각하는 것입니다. 많은 화염 전쟁의 주제였습니다.

관용구에 익숙해지면 유용 할 수 있습니다. 명백한 이점 중 하나는 코더가 루프의 내부 내용을 실제 반복 기능과 분리하도록 강제한다는 것입니다. (OK, 그것이 이점이라고 생각합니다. 다른 사람들은 당신이 실제로 이익을 얻지 않고 코드를 자르고 있다고 말합니다).

다른 장점은 foreach를 볼 때 모든 항목이 처리되거나 예외가 발생한다는 것을 알고 있습니다.

에 대한 루프는 루프를 종료하는 몇 가지 옵션이 있습니다. 루프가 전체 과정을 실행하도록하거나 break 키워드를 사용하여 루프에서 명시 적으로 점프하거나 return 키워드를 사용 하여 전체 함수 중간 루프를 종료 할 수 있습니다. 반대로, foreach 는 이러한 옵션을 허용하지 않으므로 더 읽기 쉽습니다. 함수 이름을 한 눈에 볼 수 있으며 반복의 전체 특성을 알 수 있습니다.

혼란스러운 for 루프 의 예는 다음과 같습니다 .

for(std::vector<widget>::iterator i = v.begin(); i != v.end(); ++i)
{
   /////////////////////////////////////////////////////////////////////
   // Imagine a page of code here by programmers who don't refactor
   ///////////////////////////////////////////////////////////////////////
   if(widget->Cost < calculatedAmountSofar)
   {
        break;
   }
   ////////////////////////////////////////////////////////////////////////
   // And then some more code added by a stressed out juniour developer
   // *#&$*)#$&#(#)$#(*$&#(&*^$#(*$#)($*#(&$^#($*&#)$(#&*$&#*$#*)$(#*
   /////////////////////////////////////////////////////////////////////////
   for(std::vector<widgetPart>::iterator ip = widget.GetParts().begin(); ip != widget.GetParts().end(); ++ip)
   {
      if(ip->IsBroken())
      {
         return false;
      }
   }
}

1
당신은 좋은 지적을하지만 동기 부여의 예는 완전히 공평하지는 않습니다. 당신이 사용하는 경우 std::for_each()(즉,이 게시물의 시간) 기존의 표준에 당신은 당신이 말한대로 가독성을 장려하고 조기에 루프의 돌발 금지 명명 된 펑터를 사용해야합니다. 그러나 등가 for루프에는 함수 호출 만이 없으며 너무 빠르지 않습니다. 하지만 당신이 그 말에 훌륭한 포인트를 만든 생각 이외 std::for_each() 시행한다 전체 범위를 겪고 있습니다.
wilhelmtell

10

대부분 정확합니다. 대부분의 경우 std::for_each순 손실입니다. 나는 비교 for_each하기 위해 갈 것입니다 goto. goto가능한 가장 다양한 흐름 제어 기능을 제공합니다. 상상할 수있는 거의 모든 다른 제어 구조를 구현하는 데 사용할 수 있습니다. 그러나 그 다재다능 함 goto은 고립 된 상태 를 보는 것이이 상황에서 의도 한 바에 대해 거의 아무 것도 알려주지 않음을 의미합니다 . 결과적 goto으로, 최후의 수단을 제외하고 는 올바른 마음을 가진 사람은 거의 없습니다 .

표준 알고리즘 중에서도 for_each거의 동일한 방식입니다. 사실상 모든 것을 구현하는 데 사용할 수 있습니다. 즉, for_each이 상황에서 어떤 것이 사용되고 있는지에 대해 전혀 알 수 없습니다. 불행하게도, 사람들의 태도 for_eachgoto1970 년 정도 의 태도 에 관한 것입니다. 소수의 사람들은 최후의 수단으로 만 사용해야한다는 사실에 사로 잡혀 있었지만, 여전히 많은 사람들이 그것을 기본 알고리즘으로 생각합니다. 다른 것을 사용하는 경우는 거의 없습니다. 대부분의 시간, 심지어 한 눈에 볼 때 대안 중 하나가 크게 우수했음을 알 수 있습니다.

예를 들어, 사람들이을 사용하여 컬렉션의 내용을 인쇄하기 위해 코드를 작성하는 것을 본 횟수를 잊어 버렸습니다 for_each. 내가 본 게시물을 기반으로하여 가장 일반적인 단일 사용 일 수 있습니다 for_each. 그들은 다음과 같이 끝납니다 :

class XXX { 
// ...
public:
     std::ostream &print(std::ostream &os) { return os << "my data\n"; }
};

그리고 그 이후는 어떤 조합에 대해 요구하고있다 bind1st, mem_fun등 그들이 같은 것을 할 필요가 :

std::vector<XXX> coll;

std::for_each(coll.begin(), coll.end(), XXX::print);

작업하고의 요소를 인쇄하십시오 coll. 실제로 내가 작성한 그대로 정확하게 작동한다면 평범하지는 않지만 작동하지 않을 때 코드와 관련된 몇 가지 코드를 찾기가 어렵습니다. 그것을 붙들고있는 조각들 사이에서 진행됩니다.

다행히도 더 좋은 방법이 있습니다. XXX에 일반 스트림 삽입 기 과부하를 추가합니다.

std::ostream &operator<<(std::ostream *os, XXX const &x) { 
   return x.print(os);
}

그리고 사용 std::copy:

std::copy(coll.begin(), coll.end(), std::ostream_iterator<XXX>(std::cout, "\n"));

그 작업을 수행 -하고의 내용을 인쇄하는 것을 알아 내기 위해 모두에서 실질적으로 일을지지 않습니다 coll에를 std::cout.


+1, 한 가지 실수가 있습니다. 첫 번째 예에서 그것은boost::mem_fn(&XXX::print)XXX::print
실마리

그렇기 때문에 나는 그 예제가 효과가 없다고 말했고, 그들이 효과를 발휘할 수 있도록 도움을 요청하는 글을 게시하고 있습니다 std::cout.
Jerry Coffin

1
일반적으로 좋은 대답이지만 질문은 for_each의 값이나 다른 표준 알고리즘과 비교 한 값이 아니라 for 루프와 비교 한 값에 관한 것입니다. 다른 표준 알고리즘이 적용되지 않는 경우에 생각할 수 있습니다. 그런 다음 for_each 또는 for 루프를 사용 하시겠습니까? 그것에 대해 생각해보고 당신이 생각 해낸 것이 무엇이든지 당신의 대답이되어야합니다.
Christian Rau

@ChristianRau : 질문에 정확히 답하는 것과 유용한 정보를 제공하는 것 사이에는 항상 좋은 선이 있습니다. 그가 물었던 질문들에 대한 직접적인 대답은 "아마도. 누가 알겠는가?"일 것이지만, 귀찮게하기에는 너무 쓸모가 없을 것입니다. 동시에 너무 멀리 떨어져있는 것 (예를 들어, 위의 어떤 것 대신 하스켈을 추천하는 것)도 많이 사용되지 않을 것입니다.
Jerry Coffin

3
@ChristianRau : "... 대부분 std :: for_each는 순 손실입니다"라고 std :: for_each가 이점을 제공하는지에 대한 질문을 다루지 않는다는 것을 어떻게 알 수 있습니까?
Jerry Coffin

8

가독성을 높이기위한 기능 작성의 이점은 언제 for(...)for_each(...)에 나타나지 않을 수 있습니다 .

for-loops를 사용하는 대신 functional.h의 모든 알고리즘을 사용하면 코드를 훨씬 더 읽기 쉽게 할 수 있습니다.

iterator longest_tree = std::max_element(forest.begin(), forest.end(), ...);
iterator first_leaf_tree = std::find_if(forest.begin(), forest.end(), ...);
std::transform(forest.begin(), forest.end(), firewood.begin(), ...);
std::for_each(forest.begin(), forest.end(), make_plywood);

이다 훨씬 더 읽기보다는;

Forest::iterator longest_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
   if (*it > *longest_tree) {
     longest_tree = it;
   }
}

Forest::iterator leaf_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
   if (it->type() == LEAF_TREE) {
     leaf_tree  = it;
     break;
   }
}

for (Forest::const_iterator it = forest.begin(), jt = firewood.begin(); 
     it != forest.end(); 
     it++, jt++) {
          *jt = boost::transformtowood(*it);
    }

for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
    std::makeplywood(*it);
}

그리고 그것이 내가 생각하는 것입니다 .for 루프를 한 줄 함수로 일반화하십시오 =)


6

쉬움 : for_each모든 배열 항목을 처리하는 함수가 이미있을 때 유용하므로 람다를 작성할 필요가 없습니다. 확실히, 이것은

for_each(a.begin(), a.end(), a_item_handler);

~보다 낫다

for(auto& item: a) {
    a_item_handler(a);
}

또한, 원거리 for루프는 전체 컨테이너를 처음부터 끝까지 반복하는 반면, for_each더 유연합니다.


4

for_each루프 작동에 대한 명확한 의미를 사용자 코드에서 반복자 (루프 구현 방법의 세부 사항) 숨기고 정의하기위한 것입니다 : 각 요소는 정확히 한 번만 반복됩니다.

현재 표준에서 가독성 문제는 코드 블록 대신 마지막 인수로 functor가 필요하므로 많은 경우 특정 functor 유형을 작성해야한다는 것입니다. functor 객체를 제자리에서 정의 할 수없고 (함수 내에 정의 된 로컬 클래스를 템플릿 인수로 사용할 수 없음) 루프 구현을 실제 루프에서 멀리 옮겨야하므로 읽기 어려운 코드로 바뀝니다.

struct myfunctor {
   void operator()( int arg1 ) { code }
};
void apply( std::vector<int> const & v ) {
   // code
   std::for_each( v.begin(), v.end(), myfunctor() );
   // more code
}

각 객체에 대해 특정 작업을 수행하려는 경우 std::mem_fn, 또는 boost::bind( std::bind다음 표준에서) 또는 boost::lambda(다음 표준에서 람다)를 사용하여 간단하게 만들 수 있습니다.

void function( int value );
void apply( std::vector<X> const & v ) {
   // code
   std::for_each( v.begin(), v.end(), boost::bind( function, _1 ) );
   // code
}

호출 할 함수 / 메소드가있는 경우 핸드 롤 버전보다 덜 읽기 쉽고 컴팩트하지 않습니다. 구현시 for_each루프 의 다른 구현을 제공 할 수 있습니다 (병렬 처리 생각).

다가오는 표준은 몇 가지 단점을 다른 방식으로 처리하며 템플릿에 대한 인수로 로컬로 정의 된 클래스를 허용합니다.

void apply( std::vector<int> const & v ) {
   // code
   struct myfunctor {
      void operator()( int ) { code }
   };
   std::for_each( v.begin(), v.end(), myfunctor() );
   // code
}

코드의 지역성 향상 : 탐색 할 때 코드의 기능을 확인할 수 있습니다. 사실, functor를 정의하기 위해 클래스 구문을 사용할 필요는 없지만 람다를 사용하십시오.

void apply( std::vector<int> const & v ) {
   // code
   std::for_each( v.begin(), v.end(), 
      []( int ) { // code } );
   // code
}

경우에 따라 for_each더 자연스럽게 만드는 구체적인 구성이있을 것입니다.

void apply( std::vector<int> const & v ) {
   // code
   for ( int i : v ) {
      // code
   }
   // code
}

나는 for_each구조를 수동 롤 루프와 혼합하는 경향이 있습니다. 기존 함수 또는 메소드에 대한 호출 만 필요한 경우 ( for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )) for_each코드에서 많은 보일러 플레이트 반복기 항목을 제거 하는 구문을 사용합니다. 더 복잡한 것이 필요하고 실제 사용보다 몇 줄만 functor를 구현할 수없는 경우 내 루프를 롤링합니다 (작업을 유지합니다). 중요하지 않은 코드 섹션에서 BOOST_FOREACH와 함께 갈 수 있습니다 (동료가 나를 데려갔습니다)


3

가독성과 성능 외에도 일반적으로 간과되는 한 가지 측면은 일관성입니다. for (또는 while) 루프 오버 반복자를 구현하는 방법에는 여러 가지가 있습니다.

for (C::iterator iter = c.begin(); iter != c.end(); iter++) {
    do_something(*iter);
}

에:

C::iterator iter = c.begin();
C::iterator end = c.end();
while (iter != end) {
    do_something(*iter);
    ++iter;
}

다양한 수준의 효율성과 버그 가능성 사이에 많은 예가 있습니다.

그러나 for_each를 사용하면 루프를 추상화하여 일관성을 유지합니다.

for_each(c.begin(), c.end(), do_something);

현재 걱정해야 할 것은 Boost 또는 C ++ 0x 기능을 사용하여 루프 바디를 함수, 펑터 또는 람다로 구현합니까? 개인적으로, 무작위 for / while 루프를 구현하거나 읽는 방법보다 걱정이됩니다.


3

나는 std::for_each람다가 없으면 완전히 잘못되었다고 싫어 하고 생각했습니다. 그러나 나는 얼마 전에 내 마음을 바꾸었고, 지금은 실제로 그것을 좋아합니다. 또한 가독성을 향상시키고 TDD 방식으로 코드를 쉽게 테스트 할 수 있다고 생각합니다.

std::for_each알고리즘은 다음과 같이 읽을 수있는 범위에있는 모든 요소와 함께 할 일 , 할 수 가독성을 향상시킬 수 있습니다. 수행하려는 조치의 길이가 20 줄이고 조치가 수행되는 기능의 길이도 약 20 줄이라고 가정하십시오. 그것은 전통적인 for 루프로 40 줄 길이의 함수를 만들고, 약 20으로 만 함수 std::for_each를 이해하기 쉬울 것입니다.

펑 터는 다음 std::for_each과 같이보다 일반적이고 재사용이 가능합니다.

struct DeleteElement
{
    template <typename T>
    void operator()(const T *ptr)
    {
        delete ptr;
    }
};

그리고 코드 std::for_each(v.begin(), v.end(), DeleteElement())에는 명시 적 루프보다 약간 더 나은 IMO 와 같은 하나의 라이너 만 있습니다 .

이러한 함수는 일반적으로 긴 함수 중간에 명시 적 for 루프보다 단위 테스트를 받기가 더 쉽고 그 자체만으로도 이미 큰 승리입니다.

std::for_each 범위에 실수를 할 가능성이 적으므로 일반적으로 더 안정적입니다.

마지막으로 컴파일러는 std::for_each특정 유형의 수작업 for 루프보다 약간 더 나은 코드를 생성 할 수 있습니다. for (each) 는 컴파일러에 대해 항상 동일하게 보이고 컴파일러 작성자는 자신의 지식을 최대한 활용할 수 있도록 모든 지식을 넣을 수 있습니다. 할 수있다.

같은 같은 다른 표준 알고리즘을 적용 find_if, transform


2

for각 요소를 반복 할 수있는 for 루프입니다 .3 분의 1 등 for_each은 각 요소를 반복하기위한 것입니다. 그것의 이름에서 분명하다. 따라서 코드에서 수행하려는 작업이 더 명확합니다.


4
당신이 그것을 각각 3 씩 진행하는 반복자를 넘겨 주면 안된다 ++. 특이 할 수도 있지만 for-loop도 마찬가지입니다.
Potatoswatter

이 경우 transform누군가를 혼동하지 않기 위해 사용 하는 것이 좋습니다 .
Kirill V. Lyadvinsky

2

STL에서 다른 알고리즘을 자주 사용하는 경우 다음과 같은 장점이 있습니다 for_each.

  1. for 루프보다 더 간단하고 오류가 적은 경우가 종종 있습니다.이 인터페이스를 사용하여 기능을 사용하는 경우가 많으며, 실제로는 경우에 따라 약간 더 간결하기 때문입니다.
  2. 범위 기반 for 루프는 훨씬 간단 할 수 있지만 유연성이 떨어집니다 (Adrian McCarthy가 언급했듯이 전체 컨테이너를 반복 함).
  3. 기존의 for 루프와 달리 for_each입력 반복기에 사용할 수있는 코드를 작성해야합니다. 이런 식으로 제한되는 것은 실제로 다음과 같은 이유로 좋은 것일 수 있습니다.

    1. 나중에 다른 컨테이너에서 작동하도록 코드를 조정해야 할 수도 있습니다.
    2. 처음에, 그것은 당신에게 무언가를 가르치거나 더 나은 습관을 바꿀 수 있습니다.
    3. 항상 완벽하게 동등한 for 루프를 작성하더라도 동일한 코드를 수정하는 다른 사람들은 사용하라는 메시지가 표시되지 않으면이 작업을 수행하지 않을 수 있습니다 for_each.
  4. 사용 for_each때때로하면 같은 일을 할 수있는보다 구체적인 STL 기능을 사용할 수있는 더 분명합니다. (Jerry Coffin의 예에서와 같이; 반드시 for_each최선의 선택 인 경우 는 아니지만 for 루프가 유일한 대안은 아닙니다.)


2

C ++ 11과 두 개의 간단한 템플릿을 사용하면

        for ( auto x: range(v1+4,v1+6) ) {
                x*=2;
                cout<< x <<' ';
        }

대체 for_each또는 루프로. 왜 그것을 선택하면 간결성과 안전으로 귀결되며, 표현에 오류가 없을 가능성이 없습니다.

나를 위해, for_each루프 바디가 이미 펑 터일 때 항상 동일한 근거에서 나아졌으며, 내가 얻을 수있는 모든 이점을 활용할 것입니다.

여전히 three-expression for을 사용하지만 이제 이해해야 할 것이 있다는 것을 알면 보일러 플레이트가 아닙니다. 나는 상용구가 싫어 . 나는 그 존재를 원망했다. 그것은 실제 코드가 아니며, 그것을 읽음으로써 배울 것이 없으며, 점검해야 할 또 하나의 것입니다. 정신적 노력은 점검하는 것이 얼마나 녹슬 었는지 쉽게 측정 할 수 있습니다.

템플릿은

template<typename iter>
struct range_ { 
                iter begin() {return __beg;}    iter end(){return __end;}
            range_(iter const&beg,iter const&end) : __beg(beg),__end(end) {}
            iter __beg, __end;
};

template<typename iter>
range_<iter> range(iter const &begin, iter const &end)
    { return range_<iter>(begin,end); }

1

대부분 전체 컬렉션반복 해야합니다 . 따라서 두 개의 매개 변수 만 사용하여 고유 한 for_each () 변형을 작성하는 것이 좋습니다. 이를 통해 Terry Mahaffey의 예 를 다음과 같이 다시 작성할 수 있습니다 .

for_each(container, [](int& i) {
    i += 10;
});

나는 이것이 실제로 for 루프보다 더 읽기 쉽다고 생각합니다. 그러나 여기에는 C ++ 0x 컴파일러 확장이 필요합니다.


1

for_each가 가독성에 좋지 않다는 것을 알았습니다. 개념은 좋은 것이지만 c ++은 적어도 나에게 읽기가 매우 어렵습니다. c ++ 0x lamda 표현식이 도움이 될 것입니다. 나는 람다의 아이디어를 정말로 좋아한다. 그러나 언뜻보기에 나는 구문이 매우 추악하다고 생각하고 그것에 익숙해 질 것이라고 100 % 확신하지 못합니다. 어쩌면 5 년 후에는 익숙해 져서 다시 생각하지 않을 것입니다. 시간이 말해 줄거야 :)

나는 사용하는 것을 선호합니다

vector<thing>::iterator istart = container.begin();
vector<thing>::iterator iend = container.end();
for(vector<thing>::iterator i = istart; i != iend; ++i) {
  // Do stuff
}

시작 및 종료 반복자에 대해 명명 된 변수를 사용하여 명시 적 for 루프가 더 명확하게 읽고 명시 적으로 발견하여 for 루프의 혼란을 줄입니다.

물론 경우에 따라 다를 수 있습니다. 이것이 제가 가장 일반적으로 찾는 것입니다.


0

반복자를 루프를 통해 각 반복에서 수행되는 함수에 대한 호출로 만들 수 있습니다.

여기를 참조하십시오 : http://www.cplusplus.com/reference/algorithm/for_each/


2
링크 전용 게시물은 좋은 답변을 얻지 못합니다. 어쨌든 해당 링크의 어디에서 호출 가능한 반복자와 유사한 것이 표시됩니까? 나는 그 개념이 이해가되지 않는다고 확신한다. 어쩌면 당신은 무엇을하는지 요약했을 것 for_each입니다.이 경우 이점에 대한 질문에 대답하지 않습니다.
underscore_d


0

for_each포크 조인 패턴 을 구현할 수 있습니다. 그 외에는 유창한 인터페이스를 지원 합니다 .

포크 조인 패턴

gpu::for_each여러 작업자에서 람다 작업을 호출하여 이기종 병렬 컴퓨팅에 cuda / gpu를 사용하도록 구현 을 추가 할 수 있습니다 .

gpu::for_each(users.begin(),users.end(),update_summary);
// all summary is complete now
// go access the user-summary here.

그리고 gpu::for_each다음 명령문을 실행하기 전에 작업자가 모든 람다 작업이 완료 될 때까지 기다릴 수 있습니다.

유창한 인터페이스

이를 통해 사람이 읽을 수있는 코드를 간결하게 작성할 수 있습니다.

accounts::erase(std::remove_if(accounts.begin(),accounts.end(),used_this_year));
std::for_each(accounts.begin(),accounts.end(),mark_dormant);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.