C ++ 20 presenta std::common_reference
. ¿Cual es su propósito? ¿Alguien puede dar un ejemplo de su uso?
C ++ 20 presenta std::common_reference
. ¿Cual es su propósito? ¿Alguien puede dar un ejemplo de su uso?
Respuestas:
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: reference
y value_type
. El primero es el tipo de retorno de los iteradores operator*
, y el value_type
es 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: reference
siempre value_type
, opcionalmente, const y referencia calificada. Los primeros intentos de definir el InputIterator
concepto requerían que la expresión *it
fuera 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_iterator
que itera dos secuencias en el paso de bloqueo. Cuando desreferencia a zip_iterator
, obtienes un temporal pair
de los dos reference
tipos de iteradores . Entonces, zip
'a vector<int>
y a vector<double>
tendrían estos tipos asociados:
zip
iterador reference
: pair<int &, double &>
zip
iterador 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 it
hay algún tipo CR
que 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
}
CR
Es la referencia común. Todos los algoritmos pueden confiar en el hecho de que este tipo existe y pueden usarse std::common_reference
para calcularlo.
Entonces, ese es el papel que common_reference
juega 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 r
de tipo de acceso aleatorio R
sobre el cual no sabe nada y desea sort
el 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 T
haber std::less<T>
? Para eso usarías common_reference
, o el ayudante iter_common_reference_t
que 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 r
tiene iteradores proxy.
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 zip
vista 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 todo std::pair
, sea normativo o no, sin primero hacer la debida diligencia sobre los pros y los contras de simplemente hacer el std::pair
trabajo.
tuple
` pair
` tomato
` to
` MAH
- to
. pair
tiene esta característica agradable con la que puede acceder a los elementos con .first
y .second
. Los enlaces estructurados ayudan con la incomodidad de trabajar con tuple
s, pero no con todos.