web-dev-qa-db-fra.com

Quel est le but de C ++ 20 std :: common_reference?

C++ 20 introduit std::common_reference . Quel est son objectif? Quelqu'un peut-il donner un exemple d'utilisation?

41
康桓瑋

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 d'intérêt particulier associés: reference et value_type. Le premier est le type de retour de l'itérateur operator*, et le value_type est le 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: reference est toujours value_type, éventuellement const et référence qualifié. Les premières tentatives de définition du concept InputIterator exigeaient que l'expression *it était convertible en const value_type &, et pour les itérateurs les plus 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érencer un Zip_iterator, vous obtenez un pair temporaire des deux types d'itérateurs reference. Ainsi, Zip 'ing a vector<int> et un vector<double> aurait ces types associés:

Zip l'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 haut niveau. Et pourtant, laisser les deux types être arbitrairement différents se sent mal. Il y a clairement une certaine 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 sur les 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 & partage 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 utiliser std::common_reference pour le calculer.

Voilà donc le rôle que common_reference joue dans 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 d'accès aléatoire r de type R dont vous ne savez rien et que vous voulez sort la gamme.

Imaginez en outre que pour une raison quelconque, vous souhaitez utiliser une fonction de comparaison monomorphe, comme std::less<T>. (Vous avez peut-être effacé la plage et vous devez également effacer la fonction de comparaison et la passer par un virtual? Encore une fois, un étirement.) Que doit contenir Tstd::less<T>? Pour cela, vous utiliseriez common_reference, ou l'assistant iter_common_reference_t qui est implémenté en fonction 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.

43
Eric Niebler