¿Cuál es el propósito de C ++ 20 std :: common_reference?


Respuestas:


46

common_reference surgió de mis esfuerzos para llegar a una conceptualización de los iteradores de STL que se adapte a los iteradores proxy.

En el STL, los iteradores tienen dos tipos asociados de particular interés: referencey value_type. El primero es el tipo de retorno de los iteradores operator*, y el value_typees el tipo (sin constante, sin referencia) de los elementos de la secuencia.

Los algoritmos genéricos a menudo tienen la necesidad de hacer cosas como esta:

value_type tmp = *it;

... entonces sabemos que debe haber alguna relación entre estos dos tipos. Para los iteradores no proxy, la relación es simple: referencesiempre value_type, opcionalmente, const y referencia calificada. Los primeros intentos de definir el InputIteratorconcepto requerían que la expresión *itfuera convertible a const value_type &, y para la mayoría de los iteradores interesantes eso es suficiente.

Quería que los iteradores en C ++ 20 fueran más potentes que esto. Por ejemplo, considere las necesidades de un zip_iteratorque itera dos secuencias en el paso de bloqueo. Cuando desreferencia a zip_iterator, obtienes un temporal pairde los dos referencetipos de iteradores . Entonces, zip'a vector<int>y a vector<double>tendrían estos tipos asociados:

zipiterador reference: pair<int &, double &>
zipiterador value_type:pair<int, double>

Como puede ver, estos dos tipos no están relacionados entre sí simplemente agregando la calificación de cv y ref de nivel superior. Y sin embargo, dejar que los dos tipos sean arbitrariamente diferentes se siente mal. Claramente hay alguna relación aquí. Pero, ¿cuál es la relación y qué pueden asumir los algoritmos genéricos que operan en iteradores con seguridad sobre los dos tipos?

La respuesta en C ++ 20 es que para cualquier tipo de iterador válido, proxy o no, los tipos reference &&y value_type &comparten una referencia común . En otras palabras, para algún iterador ithay algún tipo CRque hace que lo siguiente esté bien formado:

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
}

CREs la referencia común. Todos los algoritmos pueden confiar en el hecho de que este tipo existe y pueden usarse std::common_referencepara calcularlo.

Entonces, ese es el papel que common_referencejuega en el STL en C ++ 20. En general, a menos que esté escribiendo algoritmos genéricos o iteradores proxy, puede ignorarlo con seguridad. Está allí debajo de las cubiertas para garantizar que sus iteradores cumplan con sus obligaciones contractuales.


EDITAR: El OP también pidió un ejemplo. Esto es un poco artificial, pero imagine que es C ++ 20 y se le da un rango rde tipo de acceso aleatorio Rsobre el cual no sabe nada y desea sortel rango.

Además, imagine que por alguna razón, desea utilizar una función de comparación monomórfica, como std::less<T>. (Tal vez ha borrado el rango, y necesita borrar también la función de comparación y pasarla a virtual? De nuevo, un tramo.) ¿Qué debería Thaber std::less<T>? Para eso usarías common_reference, o el ayudante iter_common_reference_tque se implementa en términos de ello.

using CR = std::iter_common_reference_t<std::ranges::iterator_t<R>>;
std::ranges::sort(r, std::less<CR>{});

Se garantiza que funcionará, incluso si el rango rtiene iteradores proxy.


2
Tal vez soy denso, pero ¿puedes aclarar cuál es la referencia común en el ejemplo de par zip?
happydave

44
Idealmente, pair<T&,U&>y pair<T,U>&tendría una referencia común, y sería simplemente pair<T&,U&>. Sin embargo, para std::pair, no hay conversión de pair<T,U>&a pair<T&,U&>aunque tal conversión es sólida en principio. (Esto, por cierto, es la razón por la que no tenemos una zipvista en C ++ 20.)
Eric Niebler

44
@EricNiebler: " Esto, por cierto, es la razón por la que no tenemos una vista zip en C ++ 20 " . ¿Hay alguna razón por la que un iterador zip tendría que usar pair, en lugar de un tipo que podría diseñarse específicamente para su propósito? , con conversiones implícitas apropiadas según sea necesario?
Nicol Bolas

55
@Nicol Bolas No hay necesidad de usar std::pair; cualquier tipo de par adecuado con las conversiones apropiadas funcionará, y range-v3 define dicho tipo de par. En el Comité, a LEWG no le gustó la idea de agregar a la Biblioteca Estándar un tipo que era casi, pero no del todo std::pair, sea normativo o no, sin primero hacer la debida diligencia sobre los pros y los contras de simplemente hacer el std::pairtrabajo.
Eric Niebler el

3
tuple` pair` tomato` to` MAH- to. pairtiene esta característica agradable con la que puede acceder a los elementos con .firsty .second. Los enlaces estructurados ayudan con la incomodidad de trabajar con tuples, pero no con todos.
Eric Niebler
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.