C ++ 20 presenta std::common_reference. ¿Cual es su propósito? ¿Alguien puede dar un ejemplo de su uso?
fuente
C ++ 20 presenta std::common_reference. ¿Cual es su propósito? ¿Alguien puede dar un ejemplo de su uso?
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.
pair<T&,U&>ypair<T,U>&tendría una referencia común, y sería simplementepair<T&,U&>. Sin embargo, parastd::pair, no hay conversión depair<T,U>&apair<T&,U&>aunque tal conversión es sólida en principio. (Esto, por cierto, es la razón por la que no tenemos unazipvista en C ++ 20.)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?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 todostd::pair, sea normativo o no, sin primero hacer la debida diligencia sobre los pros y los contras de simplemente hacer elstd::pairtrabajo.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 contuples, pero no con todos.