알고리즘 솔루션 :
std::generate(numbers.begin(), numbers.end(), rand);
범위 기반 for 루프 솔루션 :
for (int& x : numbers) x = rand();
std::generate
C ++ 11에서 범위 기반 for 루프에 대해 더 자세한 정보를 사용하려는 이유는 무엇 입니까?
알고리즘 솔루션 :
std::generate(numbers.begin(), numbers.end(), rand);
범위 기반 for 루프 솔루션 :
for (int& x : numbers) x = rand();
std::generate
C ++ 11에서 범위 기반 for 루프에 대해 더 자세한 정보를 사용하려는 이유는 무엇 입니까?
begin()
및 end()
?
range
그들의 도구 상자에 기능 을 가질 것으로 기대 합니다. (예 for(auto& x : range(first, last))
)
boost::generate(numbers, rand); // ♪
답변:
첫 번째 버전
std::generate(numbers.begin(), numbers.end(), rand);
일련의 값을 생성하고자 함을 알려줍니다.
두 번째 버전에서는 독자가 스스로 알아 내야합니다.
입력 시간을 절약하는 것은 대개 읽기 시간에 손실되기 때문에 차선책입니다. 대부분의 코드는 입력 한 것보다 훨씬 더 많이 읽습니다.
numbers
이유없이 두 번 지정해야하므로 깁니다 . 따라서 : boost::range::generate(numbers, rand);
. 잘 구축 된 라이브러리에서 더 짧고 읽기 쉬운 코드를 모두 가질 수없는 이유는 없습니다.
std::generate(number.begin(), numbers.begin()+3, rand)
있지 않습니까? 그래서 number
때때로 두 번 지정하는 것이 유용 할 수 있다고 생각합니다 .
std::generate()
지정할 수 있는 동시에 반복을 제거 std::generate(slice(number.begin(), 3), rand)
하는 가상 범위 슬라이싱 구문을 사용하거나 더 잘 할 수 있습니다 . 세 가지 주장에서 시작하여 반대로하는 것은 더 지루합니다. std::generate(number[0:3], rand)
number
std::generate()
for 루프가 범위 기반인지 여부에 관계없이 전혀 차이가 없지만 괄호 안의 코드 만 단순화됩니다. 알고리즘은 의도 를 표시한다는 점에서 더 명확합니다 .
개인적으로 처음 읽은 내용 :
std::generate(numbers.begin(), numbers.end(), rand);
"우리는 범위의 모든 것에 할당하고 있습니다. 범위는 numbers
입니다. 할당 된 값은 무작위입니다."
나의 초기 독서 :
for (int& x : numbers) x = rand();
"우리는 범위 내의 모든 것에 무언가를하고 있습니다. 범위는 numbers
입니다. 우리가하는 일은 임의의 값을 할당하는 것입니다."
그것들은 상당히 비슷하지만 동일하지는 않습니다. 내가 첫 번째 독서를 유발하고 싶은 한 가지 그럴듯한 이유는이 코드에 대한 가장 중요한 사실이 범위에 할당된다는 것입니다. 그래서 당신의 "내가 왜 ..." generate
C ++에서 std::generate
"범위 할당"을 의미 하기 때문에 사용 합니다. btw가하는 것처럼 std::copy
둘의 차이점은 당신이 할당하는 것입니다.
하지만 혼란스러운 요소가 있습니다. 범위 기반 for 루프는 numbers
반복기 기반 알고리즘보다 범위가임을 본질적으로보다 직접적인 방식으로 표현합니다 . 이것이 사람들이 범위 기반 알고리즘 라이브러리에서 작업하는 이유 입니다. 버전 boost::range::generate(numbers, rand);
보다 낫습니다 std::generate
.
반대로 int&
범위 기반 for 루프에는 주름이 있습니다. 범위의 값 유형이가 아닌 int
경우 여기에서 변환 가능 여부에 의존하는 성가신 미묘한 작업을 수행하는 int&
반면 generate
코드 rand
는 요소에 할당 할 수있는 결과에만 의존합니다 . 값 유형이라고 int
해도 나는 그것이 아닌지 생각하기 위해 멈출 수 있습니다. 따라서 auto
할당 된 내용을 볼 때까지 유형에 대한 생각을 연기 auto &x
합니다. "어떤 유형이 있든지간에 범위 요소에 대한 참조를 가져옵니다"라고 말합니다. (그들이있는 거 기능 템플릿 때문에)이었다 돌아 가기 C ++ 03, 알고리즘 정확한 유형을 숨길 수있는 방법, 지금은있어 방법.
나는 항상 가장 단순한 알고리즘이 동등한 루프에 비해 미미한 이점만을 가지고있는 경우라고 생각합니다. 범위 기반 for 루프는 루프를 개선합니다. 따라서 여백이 더 좁아지고 특정 경우에 마음이 바뀔 수 있습니다. 하지만 여전히 스타일 차이가 있습니다.
operator int&()
있습니까? :)
int&
되며 SomeClass&
이제 변환 연산자와 표시되지 않은 단일 매개 변수 생성자에 대해 걱정해야합니다 explicit
.
operator int&()
과 과부하로 작동 operator int const &() const
하지만 다시과 로딩 operator int() const
하여 작동 할 수 있습니다 operator=(int)
.
내 생각에, Effective STL Item 43 : "수기 루프보다 알고리즘 호출을 선호하십시오." 여전히 좋은 조언입니다.
나는 보통 begin()
/ end()
지옥을 제거하기 위해 래퍼 함수를 작성 합니다. 그렇게하면 예제는 다음과 같습니다.
my_util::generate(numbers, rand);
나는 의도 를 전달하고 가독성에서 모두 for 루프 기반 범위를 능가한다고 생각합니다 .
그렇긴하지만 C ++ 98에서 일부 STL 알고리즘 호출이 발화 할 수없는 코드를 생성했으며 "수작업으로 작성한 루프보다 알고리즘 호출 선호"를 따르는 것이 좋은 생각이 아닌 것 같음을 인정해야합니다. 다행히 람다가이를 변경했습니다.
Herb Sutter 의 다음 예제를 고려하십시오 . Lambdas, Lambdas Everywhere .
과제 : v의 첫 번째 요소 인 > x
and를 찾습니다 < y
.
람다없이 :
auto i = find_if( v.begin(), v.end(),
bind( logical_and<bool>(),
bind(greater<int>(), _1, x),
bind(less<int>(), _1, y) ) );
람다와 함께
auto i=find_if( v.begin(), v.end(), [=](int i) { return i > x && i < y; } );
에서 내 상세를 줄일 수 있지만 의견, 수동 루프는, readabitly 부족 :
for (int& x : numbers) x = rand();
나는 초기화이 루프를 사용하지 않을 일 에 의해 정의 된 범위의 숫자를 내가 볼 때, 그것이 나에게 것 때문에, 이상 반복하는을 , 숫자의 범위,하지만 실제로는 (본질적으로)하지 않는 즉, 대신 범위에서 읽기 , 그것은 쓰는 .
를 사용하면 의도가 훨씬 더 명확 해 std::generate
집니다.
1. 이 컨텍스트에서 초기화 는 컨테이너의 요소에 의미있는 값을 제공하는 것을 의미 합니다.
std::generate
C ++ 프로그래머라고 가정 할 수있는 익숙한 경우에도 동일한 작업을 수행한다는 것이 분명합니다 (익숙하지 않은 경우 검색, 동일한 결과).
&
문자 를 잊어 버리면 어떨까요?). 알고리즘의 장점은 의도를 보여주는 반면 루프를 사용하면 추론해야한다는 것입니다. 루프 구현에 버그가있는 경우 버그인지 의도적 인 것인지 명확하지 않습니다.
std::generate
, 단순한보기만으로도이 함수에 의해 무언가 가 생성되고 있다고 말할 수 있습니다 . 함수에 대한 세 번째 인수로 무엇 에 대한 답을 얻을 수 있는지 . 나는 이것이 훨씬 낫다고 생각합니다.
반복기를 입력으로 취하는 알고리즘이 할 수있는 범위 기반 루프로는 (간단히) 할 수없는 일이 있습니다. 예를 들어std::generate
같습니다.
에 컨테이너 채워 limit
제외 (를 limit
유효한 반복자에이다numbers
를 한 분포의 변수로 나머지는 다른 분포의 변수 임) 채 .
std::generate(numbers.begin(), limit, rand1);
std::generate(limit, numbers.end(), rand2);
반복자 기반 알고리즘을 사용하면 작업중인 범위를 더 잘 제어 할 수 있습니다.
의 특정 사례 std::generate
에 대해서는 가독성 / 의도 문제에 대한 이전 답변에 동의합니다. std :: generate는 나에게 더 명확한 버전으로 보입니다. 그러나 나는 이것이 맛의 문제임을 인정합니다.
즉, std :: algorithm을 버리지 않는 또 다른 이유가 있습니다. 일부 데이터 유형에 특화된 특정 알고리즘이 있습니다.
가장 간단한 예는 std::fill
. 일반 버전은 제공된 범위에 대해 for 루프로 구현되며이 버전은 템플릿을 인스턴스화 할 때 사용됩니다. 하지만 항상 그런 것은 아닙니다. 예를 들어 범위를 제공 std::vector<int>
하면 실제로 호출됩니다.memset
하여 훨씬 빠르고 더 나은 코드를 생성합니다.
그래서 저는 여기서 효율성 카드를 사용하려고합니다.
손으로 쓴 루프는 std :: algorithm 버전만큼 빠르지 만 거의 빠를 수 없습니다. 뿐만 아니라 std :: algorithm은 특정 컨테이너 및 유형에 특화 될 수 있으며 깨끗한 STL 인터페이스에서 수행됩니다.
내 대답은 아마도 아닐 것입니다. 우리가 C ++ 11에 대해 이야기하고 있다면 아마도 (아니요). 예를 들어 std::for_each
람다와 함께 사용하는 것이 정말 짜증납니다.
std::for_each(c.begin(), c.end(), [&](ExactTypeOfContainedValue& x)
{
// do stuff with x
});
그러나 범위 기반 for를 사용하는 것이 훨씬 좋습니다.
for (auto& x : c)
{
// do stuff with x
}
반면에 C ++ 1y에 대해 이야기하고 있다면 아니요, 알고리즘은 범위 기반에 의해 폐기되지 않을 것이라고 주장합니다. C ++ 표준위원회에는 C ++에 범위를 추가하는 제안을 작업하는 연구 그룹이 있으며 다형성 람다에 대한 작업도 진행 중입니다. 범위는 반복기 쌍을 사용할 필요가 없으며 다형성 람다는 람다의 정확한 인수 유형을 지정하지 않도록합니다. 이것은 다음 std::for_each
과 같이 사용될 수 있음을 의미합니다 (이것을 어려운 사실로 받아들이지 마십시오. 오늘 꿈이 어떻게 생겼는지입니다) :
std::for_each(c.range(), [](x)
{
// do stuff with x
});
[]
람다 로 작성 하여 제로 캡처를 지정 한다는 것입니다 . 즉, 루프 본문을 작성하는 것과 비교하여 어휘 적으로 나타나는 변수 조회 컨텍스트에서 코드 덩어리를 분리했습니다. 분리는 일반적으로 독자에게 유용하며 읽는 동안 생각할 필요가 없습니다.
for_each
람다와 함께 사용하더라도 여전히 무의미한 것 같습니다 . foreach + capturing lambda는 현재 범위 기반 for 루프를 작성하는 장황한 방법이며, 루프보다 약간 덜 장황한 방법이됩니다. for_each
물론 방어해야한다고 생각하는 것은 아니지만 답변을보기 전에도 질문자가 알고리즘을 이기고 싶다면 for_each
가능한 모든 대상 중에서 가장 부드러운 대상을 선택할 수 있다고 생각했습니다 ;-)
for_each
하지는 않지만 범위 기반에 비해 한 가지 작은 장점이 있습니다. parallel_ 접두사를 붙여서 더 쉽게 병렬화 할 parallel_for_each
수 있습니다 (PPL을 사용하는 경우 스레드로부터 안전하다고 가정). . :-D
std::algorithm
(또는 임의로 최적화 됨).