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::transform
unary_op
또는의 순서대로 적용 할 수있는 것은 아닙니다binary_op
. 시퀀스에 순서대로 함수를 적용하거나 시퀀스의 요소를 수정하는 함수를 적용하려면을 사용하십시오std::for_each
.
이것은 아마도 병렬 구현을 허용하는 것입니다. 그러나 세번째의 파라미터 std::transform
A는 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
대해 작업 순서에 대한 인용문이없는 경우