2010-11-08 24 views
12

en cuenta lo siguiente:std :: vector de objetos y const-corrección

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).

+5

* ¿Qué * es el error de compilación? * No seas vago, sé un [as] (http://tinyurl.com/so-hints); escriba un [apropiado] (http://sscce.org/) [test-case!] (http://www.xs4all.nl/~weegen/eelis/iso-c++/testcase.xhtml) * –

+2

Creo que tiene una opción: o pierde la const, o pierde la capacidad de usar el vector. Si comienzas a trabajar alrededor de él y permites que 'operator =' modifique el miembro const, ahora has dado los medios para que cualquier parte del código haga lo mismo. – UncleBens

+0

const int c; // no debe ser modificado! Su comentario anterior, ¿significa eso que 'c' no debería ser modificado por algo que use objetos de la clase A o por los miembros de la clase A en sí? – yasouser

Respuesta

16

no estoy seguro de por qué nadie lo dijo, pero la respuesta correcta es dejar caer el const, o almacenar A* 's en el vector (utilizando el puntero inteligente apropiado).

Puede dar a su 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 bailando alrededor de UB y código incorrecto? ¿Qué obtienes haciendo const? (Sugerencia: nada). Su problema es conceptual: Si una clase tiene un miembro const, la clase es const. Objetos que son const, fundamentalmente, no se pueden asignar.

Solo haga que sea un privado sin const, y exponga su valor inmutablemente. Para los usuarios, esto es equivalente, en lo sucesivo. Permite que las funciones generadas implícitamente funcionen bien.

+0

Esa es en realidad una solución aceptable. ¡Gracias! – eisbaw

+0

Bien dicho. Supongo que fue el comentario posterior al 'const int c' lo que me hizo asumir que no había otra solución para la situación de @eisbaw. El viejo adagio suena a verdad: "¿por qué complicar las cosas a nosotros mismos?" –

+0

En realidad, no es tan fácil "simplemente caer" const '". ¿Por qué? Debido a que eliminar 'const' en los campos de miembro efectivamente le obliga a soltar 'const' en los argumentos del constructor, lo que a su vez lo obliga a soltar 'const' en el lugar donde lo usa, etc. ... (Creo que la idea debería ser claro). La solución con std :: vector de punteros debería funcionar (aunque no es muy elegante). Por cierto, en VS2010 puede push_back elementos sin operador de asignación en el std :: vector. –

1

Probablemente el assignment operator. 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.

2

El tipo almacenado debe cumplir los requisitos CopyConstructible y asignable, lo que significa que el operador = se necesita también.

7

Falta un operador de asignación (o operador de asignación de copia), uno de the big three.

+2

+1 para un enlace al Big Three. –

15

Un elemento contenedor STL debe ser copia-construible y asignable (que tu clase no es A). Necesita sobrecargar operator =.

: §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:

Negación: No estoy seguro de si el siguiente fragmento 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; 
} 

EDITAR 2

creo que el fragmento de código anterior invoca un comportamiento indefinido porque tratar de esparcir la const-dad de una variable cualificado constUB invoca.

+0

Pero operator = cambia el contenido de * this; ¿Cómo cambiarías esto-> c? – eisbaw

+4

@eisbaw: Lo siento pero no se puede asignar a 'this-> c' [porque es una constante] dentro del operador' sobrecargado ''. –

+1

@eisbaw: ¿No podrías escribir un operador vacío = que simplemente ignora lo que le pases? Eso mantiene la interfaz requerida, pero conserva la integridad de c. – JTeagle

0

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 copia declarado implícitamente-es implícitamente definido cuando un objeto de su tipo de clase se le asigna un valor de su tipo de clase o una valor de un tipo de clase derivado de su tipo de clase.Un programa está Formato incorrecto si la clase para la que un operador de asignación copia es implícitamente definido tiene:

- un miembro no estático de datos de tipo const, o

- un conjunto de datos no estáticos miembro de tipo de referencia o

- un miembro de datos no estático de tipo de clase (o serie de los mismos) con un operador inaccesible de asignación de copia, o

- una clase base con un operador de asignación de copias inaccesible.

0

También es necesario 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 convierte de nuevo a una forma grabable, para ocasiones como esta .

Cabe señalar que const_cast no siempre es seguro de usar, consulte here.

0

Solución sin const_cast.

A& operator=(const A& right) 
{ 
    if (this == &right) return *this; 
    this->~A(); 
    new (this) A(right); 
    return *this; 
} 
+1

¿No es eso simplemente reemplazar un tipo de Comportamiento indefinido con otro? – TheUndeadFish

+1

'A const obj (42); obj = obj; ' –

+0

@Roger Pate. Derecha. El cheque es necesario. Lo arreglé –

0

Recientemente me encontré con la misma situación y utilicé un conjunto std ::, porque su mecanismo para agregar un elemento (inserción) no requiere el operador = (usa el operador <), a diferencia del mecanismo del vector (push_back) .

Si el rendimiento es un problema, puede probar unordered_set o algo similar.