2011-02-09 10 views
11

Los contenedores STL requieren que los valores almacenados sean copiables y asignables. const T obviamente no es un tipo asignable para cualquier T, pero traté de usarlo (simplemente siendo curioso) y descubrí que compila y, además, se comporta como un tipo asignable.VC++ permite usar tipos const para contenedores STL. ¿Por qué?

vector<const int> v(1); 
v[0] = 17; 

dirige este éxito en Visual Studio 2008 y asigna v [0] a 17.

+1

No solo eso - VS 2010 le permite * asignar nuevos valores * a 'v [0]'. ¡Intentalo! –

+1

Tengo curiosidad por lo que VC da para typeid (vector :: value_type) .name() (y o bien lo compara con typeid (vector :: value_type) .name() o demangle it). –

+0

Uno y el mismo: 'int'. –

Respuesta

1

Esto simplemente no debería funcionar. En §23.1 ¶ 3 se especifica, como usted dijo, que los objetos almacenados en un contenedor deben ser CopyConstructible (como se especifica en §20.1.3) y Assignable.

Los requisitos para un tipo AssignableT son que, al ser t y u de tipo T, que puede hacer:

t = u 

tener un T& como valor de retorno y t equivalente a u como condición posterior. (§23.1 ¶4)

Por lo tanto, const tipos son claramente no Assignable, ya que de t = u levantaría un error de compilación (§7.1.5.1 ¶5).

Supongo que esto es un error en la implementación de Microsoft. g ++ en Linux emite el típico error de plantilla de 25 kajillion-lines si incluso intentas instanciar un vector<const int> (probado con y sin el indicador -std=c++0x, por las dudas).

Por cierto, esto también se explica en detalle en este IBM FAQ.


En teoría, como dijo @ James McNellis, no se requiere el compilador para hacer estallar en la creación de instancias de vectores (si se trata de un comportamiento indefinido puede pasar cualquier cosa - incluyendo todo funcionando muy bien); sin embargo, en la declaración de asignación hay una violación de la norma que debería producir un error de compilación.

De hecho, el miembro operator[] devuelve vector<const int>::reference; se requiere que sea un valor l de T (§23.1 ¶5 tabla 66); ya que T es del tipo const, será un valor const lvalue. Así que nos limitamos a (§7.1.5.1 ¶5), que define el código que intenta realizar una asignación a un elemento const como "mal formado", y esto exige un error de compilación o al menos una advertencia, porque la asignación a - const es una regla diagnosticable (§1.4 ¶1-2) (no se especifica la declaración "no se requiere diagnóstico").


edición final

En realidad, @ James McNellis es correcto; una vez que ha invocado un comportamiento indefinido al crear instancias de vector<const int>, las reglas habituales dejan de tener valor, por lo que la implementación sigue cumpliendo los estándares, haga lo que haga, incluida la eliminación del const del tipo de elemento o la generación de los demonios nasales habituales.

+1

Un fácil La forma de hacerlo funcionar sería un rasgo de tipo remove_const (este rasgo exacto debería estar en 0x, pero quizás con un nombre diferente) en lugar de mutable. –

+0

@Fred Nurk: probablemente sea como dijiste. De hecho, creo que la cosa 'mutable' que escribí fue un brainfart, ya que' mutable' se pone en campos de clase non-'const' (§7.1.2 ¶8) para permitir su modificación mediante los métodos 'const', y esto no tiene nada que ver con eliminar el 'const' de los tipos de plantilla. –

+0

Esto no es un error en la implementación. El programa de OP exhibe un comportamiento indefinido. –

12

Esto no es un error en la implementación como otros han sugerido.

La violación de los requisitos de una biblioteca estándar de C++ no hace que su programa se vuelva mal formado, produce un comportamiento indefinido.

Ha incumplido el requisito de que el tipo de valor almacenado en un contenedor debe ser copiable y asignable (los tipos const no son asignables, obviamente), por lo que su programa muestra un comportamiento indefinido.

El idioma aplicable a partir del estándar de C++ se puede encontrar en C++ 03 17.4.3.6 [lib.res.on.functions]:

En ciertos casos (funciones de reemplazo, funciones de controlador, las operaciones en tipos utilizados para crear instancias de componentes de plantilla de biblioteca estándar), la biblioteca estándar de C++ depende de los componentes proporcionados por un programa C++. Si estos componentes no cumplen con sus requisitos, el Estándar no establece requisitos sobre la implementación.

En particular, los efectos no están definidos en los siguientes casos:

...

  • para los tipos utilizados como argumentos de plantilla al crear instancias de un componente de la plantilla, si las operaciones sobre el tipo no implementan la semántica de la subcláusula de requisitos aplicable.

La aplicación de Visual C++ biblioteca estándar puede hacer nada con este código, incluyendo la eliminación de silencio o haciendo caso omiso de la const-calificación, y es todavía normas conformes.

+3

En mi opinión más humilde, la eliminación de calificación silenciosa aquí es realmente fea. –

+0

La eliminación de la calificación en realidad puede romper el código perverso pero uniforme de las normas.Podría escribir una clase que tenga un operador de asignación de copias constinado que garantice 't == u' después de' t = u' _y_ un operador de asignación de copias no calificado que no garantice esa condición posterior. De esta forma, 'const T' sería asignable, pero' T' no lo sería. No puedo imaginar ningún caso de uso válido para esto, y si veo un código así probablemente llore. –

+0

Como el estándar define "asignable", parece que la condición posterior es que t y u son equivalentes. - Por cierto, ¿C++ 0x va a eliminar el requisito de asignación? Ya no puedo verlo en el borrador, y tendría sentido que las operaciones que no requieren asignación funcionen. – UncleBens

Cuestiones relacionadas