Cppreference a cet exemple de code pour 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; });
Mais cela dit aussi:
std::transform
Ne garantit pas l'application dans l'ordre deunary_op
Oubinary_op
. Pour appliquer une fonction à une séquence dans l'ordre ou pour appliquer une fonction qui modifie les éléments d'une séquence, utilisezstd::for_each
.
C'est probablement pour permettre des implémentations parallèles. Cependant, le troisième paramètre de std::transform
Est un LegacyOutputIterator
qui a la postcondition suivante pour ++r
:
Après cette opération, il n'est pas nécessaire que
r
soit incrémentable et aucune copie de la valeur précédente der
ne doit plus être déréférencable ou incrémentable.
Il me semble donc que l'affectation de la sortie doit se produire dans l'ordre. Signifient-ils simplement que l'application de unary_op
Peut être hors service et stockée dans un emplacement temporaire, mais copiée dans la sortie dans l'ordre? Cela ne ressemble pas à quelque chose que vous voudriez faire.
La plupart des bibliothèques C++ n'ont pas encore implémenté d'exécuteurs parallèles, mais Microsoft l'a fait. Je suis assez sûr this est le code pertinent, et je pense qu'il appelle cette fonction populate()
function pour enregistrer des itérateurs en morceaux de la sortie, ce qui n'est sûrement pas une chose valide à faire car LegacyOutputIterator
peut être invalidé en incrémentant des copies de celui-ci.
Qu'est-ce que je rate?
Je crois que la transformation est garantie d'être traitée dans l'ordre . std::back_inserter_iterator
est un itérateur de sortie (son iterator_category
le type de membre est un alias pour std::output_iterator_tag
) selon [back.insert.iterator] .
Par conséquent, std::transform
n'a aucune autre option sur la façon de procéder à l'itération suivante que d'appeler le membre operator++
sur le paramètre result
.
Bien sûr, cela n'est valable que pour les surcharges sans politique d'exécution, où std::back_inserter_iterator
ne peut pas être utilisé (ce n'est pas un itérateur de transfert).
BTW, je ne voudrais pas argumenter avec des citations de cppreference. Les déclarations y sont souvent imprécises ou simplifiées. Dans de tels cas, il est préférable de regarder la norme C++. Où, en ce qui concerne std::transform
, il n'y a pas de citation sur l'ordre des opérations.
Donc, ce que j'ai manqué, c'est que les versions parallèles prennent LegacyForwardIterator
s, pas LegacyOutputIterator
. Un LegacyForwardIterator
peut être incrémenté sans invalider des copies de celui-ci, il est donc facile de l'utiliser pour implémenter un parallèle désordonné std::transform
.
Je pense que les versions non parallèles de std::transform
doit être exécuté dans l'ordre. Soit cppreference est erroné, soit la norme laisse simplement cette exigence implicite car il n'y a pas d'autre moyen de l'implémenter. (Le fusil de chasse ne parcourt pas la norme pour le savoir!)