Présentation de C ++ 20 std::common_reference
. Quel est son but? Quelqu'un peut-il donner un exemple d'utilisation?
Présentation de C ++ 20 std::common_reference
. Quel est son but? Quelqu'un peut-il donner un exemple d'utilisation?
common_reference
est né de mes efforts pour proposer une conceptualisation des itérateurs de STL qui prend en charge les itérateurs proxy.
Dans la STL, les itérateurs ont deux types associés d'intérêt particulier: reference
et value_type
. Le premier est le type de retour de l'itérateur operator*
et le type value_type
(non const, non-reference) des éléments de la séquence.
Les algorithmes génériques ont souvent besoin de faire des choses comme ceci:
value_type tmp = *it;
... donc nous savons qu'il doit y avoir une relation entre ces deux types. Pour les itérateurs non proxy, la relation est simple: elle reference
est toujours value_type
, éventuellement const et qualifiée de référence. Les premières tentatives de définition du InputIterator
concept ont exigé que l'expression *it
soit convertible const value_type &
et, pour la plupart des itérateurs intéressants, cela suffit.
Je voulais que les itérateurs en C ++ 20 soient plus puissants que cela. Par exemple, considérez les besoins d'un zip_iterator
qui itère deux séquences en étape de verrouillage. Lorsque vous déréférencez a zip_iterator
, vous obtenez un temporaire pair
des deux reference
types d' itérateurs . Ainsi, zip
'ing a vector<int>
et a vector<double>
auraient ces types associés:
zip
itérateur reference
: pair<int &, double &>
zip
itérateur value_type
:pair<int, double>
Comme vous pouvez le voir, ces deux types ne sont pas liés l'un à l'autre simplement en ajoutant une qualification CV et REF de niveau supérieur. Et pourtant, laisser les deux types être arbitrairement différents se sent mal. Il y a clairement une relation ici. Mais quelle est la relation et que peuvent supposer en toute sécurité les algorithmes génériques qui opèrent sur les itérateurs au sujet des deux types?
La réponse en C ++ 20 est que pour tout type d'itérateur valide, proxy ou non, les types reference &&
et value_type &
partagent une référence commune . En d'autres termes, pour certains itérateurs, it
il existe un type CR
qui rend les éléments suivants bien formés:
void foo(CR) // CR is the common reference for iterator I
{}
void algo( I it, iter_value_t<I> val )
{
foo(val); // OK, lvalue to value_type convertible to CR
foo(*it); // OK, reference convertible to CR
}
CR
est la référence commune. Tous les algorithmes peuvent s'appuyer sur le fait que ce type existe et peuvent être utilisés std::common_reference
pour le calculer.
C'est donc le rôle que common_reference
joue la STL en C ++ 20. Généralement, sauf si vous écrivez des algorithmes génériques ou des itérateurs proxy, vous pouvez l'ignorer en toute sécurité. Il est là sous les couvertures garantissant que vos itérateurs respectent leurs obligations contractuelles.
EDIT: Le PO a également demandé un exemple. C'est un peu artificiel, mais imaginez qu'il s'agit de C ++ 20 et que vous disposez d'une plage r
de type R
à accès aléatoire dont vous ne savez rien et que vous souhaitez accéder à sort
la plage.
Imaginez en outre que pour une raison quelconque, vous souhaitez utiliser une fonction de comparaison monomorphe, comme std::less<T>
. (Peut-être avez-vous effacé la plage et vous devez également effacer la fonction de comparaison et la passer par un virtual
? Encore une fois.) Que devrait T
contenir std::less<T>
? Pour cela, vous utiliseriez common_reference
, ou l'aide iter_common_reference_t
qui est implémentée en termes de celui-ci.
using CR = std::iter_common_reference_t<std::ranges::iterator_t<R>>;
std::ranges::sort(r, std::less<CR>{});
Cela est garanti de fonctionner, même si la plage r
a des itérateurs proxy.
pair<T&,U&>
etpair<T,U>&
aurait une référence commune, et ce serait simplementpair<T&,U&>
. Cependant, pourstd::pair
, il n'y a pas de conversion depair<T,U>&
àpair<T&,U&>
même si une telle conversion est saine en principe. (C'est d'ailleurs la raison pour laquelle nous n'avons pas dezip
vue en C ++ 20.)pair
, au lieu d'un type qui pourrait être spécifiquement conçu pour son usage , avec des conversions implicites appropriées selon les besoins?std::pair
; tout type de paire approprié avec les conversions appropriées fera l'affaire, et range-v3 définit un tel type de paire. Au sein du comité, le LEWG n'aimait pas l'idée d'ajouter à la bibliothèque standard un type qui était presque mais pas tout à faitstd::pair
, qu'il soit normatif ou non, sans d'abord faire preuve de diligence raisonnable sur les avantages et les inconvénients de simplement faire dustd::pair
travail.tuple
,pair
,tomato
,to
-MAH
-to
.pair
a cette fonctionnalité intéressante que vous pouvez accéder aux éléments avec.first
et.second
. Les liaisons structurées contribuent à certaines des maladresses de travailler avectuple
s, mais pas toutes.