std :: back_inserter와 함께 std :: transform을 사용하는 것이 유효합니까? 세번째의 파라미터 std::transformA는

Cppreference에는 다음에 대한 예제 코드가 있습니다 std::transform.

std::vector<std::size_t> ordinals;
std::transform(s.begin(), s.end(), std::back_inserter(ordinals),
               [](unsigned char c) -> std::size_t { return c; });

그러나 그것은 또한 말합니다 :

std::transformunary_op또는의 순서대로 적용 할 수있는 것은 아닙니다 binary_op. 시퀀스에 순서대로 함수를 적용하거나 시퀀스의 요소를 수정하는 함수를 적용하려면을 사용하십시오 std::for_each.

이것은 아마도 병렬 구현을 허용하는 것입니다. 그러나 세번째의 파라미터 std::transformA는 LegacyOutputIterator에 대해 다음 사후을 갖는다 ++r:

이 조작 후에 r증분 할 r필요가없고 이전 값의 사본을 더 이상 역 참조 또는 증분 할 필요가 없습니다.

따라서 출력 할당이 순서대로 이루어져야한다고 생각합니다 . 그것들은 단순히 응용 프로그램의 unary_op순서가 잘못되어 임시 위치에 저장되었지만 출력에 순서대로 복사된다는 것을 의미합니까? 그것은 당신이하고 싶은 것 같지 않습니다.

대부분의 C ++ 라이브러리는 실제로 병렬 실행기를 아직 구현하지 않았지만 Microsoft는 아직 구현했습니다. 나는 이것이 관련 코드 라고 확신 하며, 반복적으로 출력 청크에 반복자를 기록하기 위해이 함수 를 호출 한다고 생각 합니다.populate()LegacyOutputIterator

내가 무엇을 놓치고 있습니까?



답변

1) 표준의 출력 반복자 요구 사항이 완전히 깨졌습니다. LWG2035를 참조하십시오 .

2) 순전히 출력 반복자와 순전히 입력 소스 범위를 사용하는 경우 알고리즘이 실제로 할 수있는 일은 거의 없습니다. 순서대로 쓰는 것 외에는 선택의 여지가 없습니다. (그러나 가상 구현은 자체 유형을 특수한 경우로 선택할 수 있습니다 std::back_insert_iterator<std::vector<size_t>>. 어떤 구현이 여기에서 원하는지 모르겠지만 그렇게 할 수는 있습니다.)

3) 표준에서 보증을 transform순서대로 적용하는 것은 없습니다 . 우리는 구현 세부 사항을보고 있습니다.

std::transform이 같은 경우에 더 높은 반복자의 강점과 재정렬 작업을 감지 할 수없는 말은하지 않는 경우에만 출력 반복자를 필요로한다. 실제로, 알고리즘은 반복자 강도에 파견 모든 시간 , 그들은 (포인터 또는 벡터 반복자 같은) 특별한 반복자 유형에 대한 특별한 취급이 모든 시간을 .

표준은 특정 주문을 보장하고자 할 때, 어떻게 말하는지 알고 있습니다 ( std::copy‘시작 first및 진행 ‘ 참조 last).


답변

보낸 사람 n4385:

§25.6.4 변환 :

template<class InputIterator, class OutputIterator, class UnaryOperation>
constexpr OutputIterator
transform(InputIterator first1, InputIterator last1, OutputIterator result, UnaryOperation op);

template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2, class UnaryOperation>
ForwardIterator2
transform(ExecutionPolicy&& exec, ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 result, UnaryOperation op);

template<class InputIterator1, class InputIterator2, class OutputIterator, class BinaryOperation>
constexpr OutputIterator
transform(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, OutputIterator result, BinaryOperation binary_op);

template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2, class ForwardIterator, class BinaryOperation>
ForwardIterator
transform(ExecutionPolicy&& exec, ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2, ForwardIterator result, BinaryOperation binary_op);

§23.5.2.1.2 back_inserter

template<class Container>
constexpr back_insert_iterator<Container> back_inserter(Container& x);

back_insert_iterator (x)를 리턴합니다.

§23.5.2.1 클래스 템플릿 back_insert_iterator

using iterator_category = output_iterator_tag;

따라서 std::back_inserter병렬 버전의와 함께 사용할 수 없습니다 std::transform. 출력 반복자를 지원하는 버전은 입력 반복기와 함께 소스에서 읽습니다. 입력 반복자는 사전 및 사후 증분 (§23.3.5.2 입력 반복자) 만 가능하고 순차적 ( 즉, 비 병렬) 실행 만 있기 때문에 순서와 출력 반복자 사이에 순서를 유지해야합니다.


답변

그래서 내가 놓친 것은 병렬 버전이 LegacyForwardIterator아니라 LegacyOutputIterator. A LegacyForwardIterator 복사본을 무효화하지 않고 증분 수 있으므로이 명령을 사용하여 비 순차 병렬을 쉽게 구현할 수 std::transform있습니다.

병렬이 아닌 버전을 순서대로 실행 std::transform 해야 한다고 생각합니다 . cppreference가 잘못되었거나 표준은이 요구 사항을 구현하는 다른 방법이 없기 때문에이 요구 사항을 암시 적으로 남겨 둡니다. (Shotgun은 표준을 찾지 못하고 있습니다!)


답변

나는 변환 이 순서대로 처리 될 것이라고 믿습니다 . std::back_inserter_iterator출력 반복자 (그것의 iterator_category부재 형이 별명이다 std::output_iterator_tag)에 따라 [back.insert.iterator] .

따라서, std::transform이없는 다른 옵션 콜 회원에게보다 다음 반복으로 진행하는 방법에 대한 operator++상의 result매개 변수를.

물론 이것은 실행 정책이없는 과부하에만 유효합니다 std::back_inserter_iterator( 포워드 반복자가 아님).


BTW, 나는 cppreference의 따옴표로 논쟁하지 않을 것입니다. 거기에 언급 된 내용은 종종 부정확하거나 간결합니다. 이러한 경우 C ++ 표준을 보는 것이 좋습니다. 에 std::transform대해 작업 순서에 대한 인용문이없는 경우