c++ - que - ¿Cuál es la diferencia entre asignar std:: tie y tuple de referencias?
que es el paso por referencia en programacion (4)
Estoy un poco desconcertado por el siguiente negocio de tuplas:
int testint = 1;
float testfloat = .1f;
std::tie( testint, testfloat ) = std::make_tuple( testint, testfloat );
std::tuple<int&, float&> test = std::make_tuple( testint, testfloat );
Con std::tie
funciona, pero no se compila asignando directamente a la tupla de referencias, lo que da
"error: conversión de ''std :: tuple <int, float>'' a tipo no escalar ''std :: tuple <int &, float &>'' required"
o
"no hay una conversión adecuada definida por el usuario de std :: tuple <int, float> a std :: tuple <int &, float &>"
¿Por qué? Comprobé dos veces con el compilador si realmente es el mismo tipo al que se asigna al hacer esto:
static_assert( std::is_same<decltype( std::tie( testint, testfloat ) ), std::tuple<int&, float&>>::value, "??" );
Que se evalúa como cierto.
También verifiqué en línea para ver si quizás fue culpa de msvc, pero todos los compiladores dan el mismo resultado.
El problema es que std::make_tuple(testint, testfloat)
no devuelve una matriz de referencias, devuelve std::tuple<int, int>
, que es un temporal cuyos valores no pueden vincularse a lvalue-references . Si necesita una tupla de referencias, puede usar la función auxiliar std::ref
:
auto test = std::make_tuple(std::ref(a), std::ref(b));
// ^^^^^^^^^^^ ^^^^^^^^^^^
La diferencia entre esto y tie
es que las referencias son inicializadas por std::tie(a, b)
en la construcción.
La función std::tie()
realidad inicializa los miembros de la std::tuple<T&...>
de las referencias donde está la std::tuple<T&...>
no se puede inicializar con un stt templatory std::tuple<T...>
. La operación std::tie()
hace y la inicialización de un objeto correspondiente se expresaría así:
std::tuple<int&, float&> test =
std::tuple<int&, float&>(testint, testfloat) = std::make_tuple(testint, testfloat);
(obviamente, normalmente usaría valores diferentes a los de las variables ya unidas).
Supongo que, porque son tipos diferentes y no hay conversión de uno a otro, pero hay un operador de asignación de copia con plantilla, que funciona en caso de un empate.
Comprobando el código
#include <tuple>
#include <iostream>
int main() {
std::tuple<int> a{};
std::cout << std::get<0>(a) << std::endl;
std::tuple<float> b{1.f}; //note float type
a = b;
std::cout << std::get<0>(a) << std::endl;
}
output: 0 1
sugiere, que probablemente sea correcto.
Tanto make_tuple
como tie
deducirán el tipo devuelto por argumentos. Pero tie
hará un tipo de referencia make_tuple
según el tipo deducido y make_tuple
hará una tupla real.
std::tuple<int&, float&> a = std::tie( testint, testfloat );
std::tuple<int , float > b = std::make_tuple( testint, testfloat );
El objetivo del tie
es hacer una tupla temporal para evitar copias temporales de los objetos vinculados, el efecto negativo es que no puede return
un tie
si los objetos de entrada son temporales locales.