c++ - std:: vector de objetos y const-correctness
(9)
Creo que la implementación STL de las funciones vectoriales que está utilizando requiere un operador de asignación (consulte la cita de Prasoon del Estándar). Sin embargo, según la cita siguiente, dado que el operador de asignación en su código está implícitamente definido (ya que no está definido explícitamente), su programa está mal formado debido al hecho de que su clase también tiene un miembro de datos no estáticos.
C ++ 03
$ 12.8 / 12 - "Un operador de asignación de copia declarado implícitamente se define implícitamente cuando a un objeto de su clase se le asigna un valor de su tipo de clase o un valor de un tipo de clase derivado de su tipo de clase. para lo cual un operador de asignación de copia está implícitamente definido tiene:
- un miembro de datos no estáticos de tipo const, o
- un miembro de datos no estático del tipo de referencia, o
- un miembro de datos no estático del tipo de clase (o conjunto de ellos) con un operador de asignación de copia inaccesible, o
- una clase base con un operador de asignación de copia inaccesible.
Considera lo siguiente:
class A {
public:
const int c; // must not be modified!
A(int _c)
: c(_c)
{
// Nothing here
}
A(const A& copy)
: c(copy.c)
{
// Nothing here
}
};
int main(int argc, char *argv[])
{
A foo(1337);
vector<A> vec;
vec.push_back(foo); // <-- compile error!
return 0;
}
Obviamente, el constructor de copia no es suficiente. ¿Qué me estoy perdiendo?
EDITAR: Ofc. No puedo cambiar esto-> c en el método operator = (), así que no veo cómo se usará operator = () (aunque es requerido por std :: vector).
El tipo almacenado debe cumplir los requisitos CopyConstructible y Assignable , lo que significa que operator = es también necesario.
No estoy seguro de por qué nadie lo dijo, pero la respuesta correcta es soltar el const
, o almacenar A*
''s en el vector (usando el puntero inteligente apropiado).
Puedes dar a tu clase una semántica terrible haciendo que "copiar" invoque a UB o no haga nada (y por lo tanto no sea una copia), pero ¿por qué todo este problema al bailar alrededor de UB y el código incorrecto? ¿Qué obtienes al hacer esa const
? (Sugerencia: nada). Su problema es conceptual: si una clase tiene un miembro constante, la clase es const. Objetos que son const, fundamentalmente, no se pueden asignar.
Simplemente conviértalo en un privado sin const y exponga su valor de manera inmutable. Para los usuarios, esto es equivalente, en lo sucesivo. Permite que las funciones generadas implícitamente funcionen bien.
Probablemente el operador de asignación . El compilador normalmente genera uno por defecto para usted, pero esa característica está deshabilitada ya que su clase tiene una semántica de copia no trivial.
Recientemente me encontré con la misma situación y usé un conjunto std :: en su lugar, porque su mecanismo para agregar un elemento (inserción) no requiere el operador = (utiliza el <operador), a diferencia del mecanismo del vector (push_back).
Si el rendimiento es un problema, puede probar unordered_set o algo similar.
Solución alternativa sin const_cast
.
A& operator=(const A& right)
{
if (this == &right) return *this;
this->~A();
new (this) A(right);
return *this;
}
También necesita implementar un constructor de copia, que se verá así:
class A {
public:
const int c; // must not be modified!
A(int _c)
...
A(const A& copy)
...
A& operator=(const A& rhs)
{
int * p_writable_c = const_cast<int *>(&c);
*p_writable_c = rhs.c;
return *this;
}
};
La plantilla especial const_cast
toma un tipo de puntero y lo devuelve a una forma grabable, para ocasiones como esta.
Cabe señalar que const_cast
no siempre es seguro de usar, ver aquí .
Te falta un operador de asignación (o operador de asignación de copias), uno de los tres grandes .
Un elemento contenedor STL debe ser copiable y asignable 1 (que su clase A
no es). Necesita sobrecargar operator =
.
1: §23.1
dice The type of objects stored in these components must meet the requirements of CopyConstructible types (20.1.3), and the additional requirements of Assignabletypes
EDITAR :
Descargo de responsabilidad : no estoy seguro de si la siguiente pieza de código es 100% segura. Si invoca UB o algo por favor avíseme.
A& operator=(const A& assign)
{
*const_cast<int*> (&c)= assign.c;
return *this;
}
EDIT 2
Creo que el fragmento de código anterior invoca un comportamiento indefinido porque tratar de descartar la const-ness de una variable const
calificada invoca UB
.