c++ - sencillos - ¿Por qué el operador[] no está sobrecargado para valores y valores?
sobrecarga de operadores unarios en c++ (2)
Los contenedores estándar de C ++ ofrecen solo una versión del operator[]
para contenedores como vector<T>
y deque<T>
. Devuelve un T&
(que no sea para vector<bool>
, que voy a ignorar), que es un lvalue. Eso significa que en código como este,
vector<BigObject> makeVector(); // factory function
auto copyOfObject = makeVector()[0]; // copy BigObject
copyOfObject
será una copia construida. Dado que makeVector()
devuelve un vector
rvalue, parece razonable esperar que copyOfObject
se construya con movimiento.
Si el operator[]
para dichos contenedores estaba sobrecargado para los objetos de rvalue y lvalue, entonces el operator[]
para los contenedores de rvalue podría devolver una referencia de rvalue, es decir, un valor de r:
template<typename T>
container {
public:
T& operator[](int index) &; // for lvalue objects
T&& operator[](int index) &&; // for rvalue objects
...
};
En ese caso, copyOfObject
se movería construido.
¿Hay alguna razón por la que este tipo de sobrecarga sea una mala idea en general? ¿Hay alguna razón por la que no se hace para los contenedores estándar en C ++ 14?
Convertir el comentario en respuesta:
No hay nada intrínsecamente malo en este enfoque; el acceso de los miembros de la clase sigue una regla similar ( E1.E2
es un xvalue si E1
es un rvalue y E2
nombra un miembro de datos no estáticos y no es una referencia, consulte [expr.ref] /4.2), y los elementos dentro de un contenedor son lógicamente similar a los miembros de datos no estáticos.
Un problema importante al hacerlo para std::vector
u otros contenedores estándar es que probablemente romperá algún código heredado. Considerar:
void foo(int &);
std::vector<int> bar();
foo(bar()[0]);
La última línea dejará de compilar si el operator[]
en un vector rvalue devolvió un xvalue. Alternativamente, y posiblemente peor, si hay una sobrecarga foo(const int &)
, en su lugar comenzará a llamar a esa función en silencio.
Además, devolver un montón de elementos en un contenedor y usar solo un elemento ya es bastante ineficiente. Es discutible que el código que hace esto probablemente no se preocupe mucho por la velocidad de todos modos, por lo que no vale la pena introducir una pequeña mejora en el rendimiento.
Creo que dejará el contenedor en un estado no válido si al mudar uno de los elementos, argumentaría la necesidad de permitir ese estado. Segundo, si alguna vez necesitas eso, ¿no puedes simplemente llamar al constructor de movimiento del nuevo objeto de esta manera?
T copyObj = std::move(makeVector()[0]);
Actualizar:
El punto más importante es, nuevamente, en mi opinión, que los contenedores son contenedores por su naturaleza, por lo que no deben modificar los elementos dentro de ellos. Solo proporcionan un almacenamiento, mecanismo de iteración, etc.