Lo he usado std::tiesin pensarlo mucho. Funciona, así que acabo de aceptar que:
auto test(){int a, b;
std::tie(a, b)= std::make_tuple(2,3);// a is now 2, b is now 3return a + b;// 5}
Pero, ¿cómo funciona esta magia negra ? ¿Cómo se crea un temporal por std::tiecambio ay b? Encuentro esto más interesante ya que es una característica de la biblioteca, no una característica del lenguaje, así que seguramente es algo que podemos implementar y entender por nosotros mismos.
Para aclarar el concepto central, reduzcamoslo a un ejemplo más básico. Aunque std::tiees útil para funciones que devuelven (una tupla de) más valores, podemos entenderlo bien con un solo valor:
int a;
std::tie(a)= std::make_tuple(24);return a;// 24
Cosas que debemos saber para seguir adelante:
std::tie construye y devuelve una tupla de referencias.
std::tuple<int>y std::tuple<int&>2 clases completamente diferentes, sin conexión entre ellos, otros que se generaron a partir de la misma plantilla, std::tuple.
tuple tiene una tupla que operator=acepta una tupla de diferentes tipos (pero el mismo número), donde cada miembro se asigna individualmente, de cppreference :
template<class...UTypes>
tuple&operator=(const tuple<UTypes...>& other );
(3) Para todo i, asigna std::get<i>(other)a std::get<i>(*this).
El siguiente paso es deshacerse de esas funciones que solo se interponen en su camino, para que podamos transformar nuestro código a esto:
int a;
std::tuple<int&>{a}= std::tuple<int>{24};return a;// 24
El siguiente paso es ver exactamente qué sucede dentro de esas estructuras. Para esto, creo 2 tipos de Tsustituyentes std::tuple<int>y Trsustituyentes std::tuple<int&>, reducidos al mínimo para nuestras operaciones:
struct T {// substituent for std::tuple<int>int x;};structTr{// substituent for std::tuple<int&>int& xr;autooperator=(const T& other){// std::get<I>(*this) = std::get<I>(other);
xr = other.x;}};auto foo(){int a;Tr{a}= T{24};return a;// 24}
Y finalmente, me gusta deshacerme de las estructuras todas juntas (bueno, no es 100% equivalente, pero es lo suficientemente cercano para nosotros y lo suficientemente explícito como para permitirlo):
Básicamente, std::tie(a)inicializa una referencia de miembro de datos a a. std::tuple<int>(24)crea un miembro de datos con valor 24, y la asignación asigna 24 a la referencia del miembro de datos en la primera estructura. Pero dado que ese miembro de datos es una referencia vinculada a a, básicamente se asigna 24a a.
Lo que me molesta es que estamos llamando al operador de asignación a un rvalue.
Adam Zahran
En esta respuesta, indicó que un contenedor no puede contener una referencia. ¿Por qué tuplepodría tener una referencia?
nn0p
6
@ nn0p std::tupleno es un contenedor, al menos no en la terminología de C ++, no es lo mismo que el std::vectory similares. Por ejemplo, no puede iterar con las formas habituales sobre una tupla porque contiene diferentes tipos de objetos.
bolov
@Adam tie (x, y) = make_pair (1,2); en realidad se convierte en std :: tie (x, y) .operator = (std :: make_pair (1, 2)), es por eso que la "asignación a un valor r" funciona XD
Ju Piece
30
Esto no responde a su pregunta de ninguna manera, pero déjeme publicarlo de todos modos porque C ++ 17 está básicamente listo (con soporte para el compilador), por lo que mientras se pregunta cómo funcionan las cosas desactualizadas, probablemente valga la pena ver cómo funciona el actual, y futura, la versión de C ++ también funciona.
Con C ++ 17 se puede hacer un scratch std::tiea favor de lo que se llama enlaces estructurados . Hacen lo mismo (bueno, no lo mismo , pero tienen el mismo efecto neto), aunque necesita escribir menos caracteres, no necesita soporte de biblioteca, y también tiene la capacidad de tomar referencias, si eso sucede. Lo que quieras.
(Tenga en cuenta que en C ++ 17 los constructores hacen deducción de argumentos, por lo que también se make_tupleha vuelto algo superfluo).
int a, b;
std::tie(a, b)= std::make_tuple(2,3);// C++17auto[c, d]= std::make_tuple(4,5);auto[e, f]= std::tuple(6,7);
std::tuple t(8,9);auto&[g, h]= t;// not possible with std::tie
Si esa última línea se compila, estoy un poco preocupado. Parece vincular una referencia a un temporal que es ilegal.
Nir Friedman
3
@Neil Tiene que ser una referencia rvalue o una referencia constante lvalue. No puede vincular una referencia lvalue a un prvalue (temporal). Aunque esto ha sido una "extensión" en MSVC durante años.
Nir Friedman
1
Probablemente también valga la pena mencionar que tie, a diferencia de los enlaces estructurados, se pueden usar de esta manera en tipos que no se pueden construir por defecto.
Dan
5
Sí, std::tie()es mucho menos útil desde C ++ 17, donde los enlaces estructurados suelen ser superiores, pero todavía tiene usos, incluida la asignación a variables existentes (no declaradas simultáneamente recientemente) y hacer de manera concisa otras cosas como intercambiar múltiples variables u otras cosas que debe asignar a referencias.
tuple
podría tener una referencia?std::tuple
no es un contenedor, al menos no en la terminología de C ++, no es lo mismo que elstd::vector
y similares. Por ejemplo, no puede iterar con las formas habituales sobre una tupla porque contiene diferentes tipos de objetos.Esto no responde a su pregunta de ninguna manera, pero déjeme publicarlo de todos modos porque C ++ 17 está básicamente listo (con soporte para el compilador), por lo que mientras se pregunta cómo funcionan las cosas desactualizadas, probablemente valga la pena ver cómo funciona el actual, y futura, la versión de C ++ también funciona.
Con C ++ 17 se puede hacer un scratch
std::tie
a favor de lo que se llama enlaces estructurados . Hacen lo mismo (bueno, no lo mismo , pero tienen el mismo efecto neto), aunque necesita escribir menos caracteres, no necesita soporte de biblioteca, y también tiene la capacidad de tomar referencias, si eso sucede. Lo que quieras.(Tenga en cuenta que en C ++ 17 los constructores hacen deducción de argumentos, por lo que también se
make_tuple
ha vuelto algo superfluo).fuente
tie
, a diferencia de los enlaces estructurados, se pueden usar de esta manera en tipos que no se pueden construir por defecto.std::tie()
es mucho menos útil desde C ++ 17, donde los enlaces estructurados suelen ser superiores, pero todavía tiene usos, incluida la asignación a variables existentes (no declaradas simultáneamente recientemente) y hacer de manera concisa otras cosas como intercambiar múltiples variables u otras cosas que debe asignar a referencias.