web-dev-qa-db-ja.com

std :: back_inserterでstd :: transformを使用することは有効ですか?

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_の3番目のパラメーターは LegacyOutputIterator であり、_++r_に対して次の事後条件があります。

この操作の後、rはインクリメント可能である必要はなく、rの以前の値のコピーは逆参照可能またはインクリメント可能である必要がなくなります。

したがって、出力の割り当てが順番に行われる必要があるように思えます。それらは単に_unary_op_のアプリケーションが故障し、一時的な場所に保存されているが、出力に順番にコピーされる可能性があることを単に意味しているのですか?それはあなたがしたいことのようには聞こえません。

ほとんどのC++ライブラリは、実際には並列実行プログラムをまだ実装していませんが、Microsoftは実装しています。私はかなり this が関連するコードであることを確信しています、そして私はthinkそれが this populate() function イテレータを出力のチャンクに記録します。LegacyOutputIteratorはそのコピーを増分することで無効化できるため、これは確かに有効なことではありません。

何が欠けていますか?

20
Timmmm

私は変換順序どおりに処理されることが保証されていますを信じています。 std::back_inserter_iterator[back.insert.iterator] によるoutputイテレータです(そのiterator_categoryメンバー型はstd::output_iterator_tagのエイリアスです)。

その結果、std::transformには、resultパラメーターでメンバーoperator++を呼び出すよりも、次の反復に進む方法について他のオプションなしがあります。

もちろん、これは、std::back_inserter_iteratorが使用されない可能性がある、実行ポリシーのないオーバーロードに対してのみ有効です(forwarding iteratorではありません)。


ところで、私はcppreferenceからの引用符で議論しません。そこにあるステートメントは、しばしば不正確または単純化されています。このような場合は、C++標準を確認することをお勧めします。ここで、std::transformに関しては、操作の順序についての引用はありません。

0
Daniel Langr

したがって、私が見逃したのは、パラレルバージョンがLegacyForwardIteratorではなく LegacyOutputIterator sをとることです。 LegacyForwardIteratorcanそれのコピーを無効にすることなくインクリメントされるため、これを使用して、順不同のパラレルstd::transformを実装するのは簡単です。

std::transformの非並列バージョンmustは順序どおりに実行されると思います。それについてcppreferenceが間違っているか、標準を実装する他の方法がないため、標準がこの要件を暗黙的に残している可能性があります。 (Shotgunは標準を調べて調べていません!)

0
Timmmm