2011-01-12 11 views
12

¿Cuáles son las deficiencias conocidas de const en C++ y C++ 0x?¿Qué pasa con const?

+3

Creo que son útiles porque hacen que el código sea más fácil de entender. – BlackBear

+8

Necesito hacer una pregunta antes de obtener su respuesta, me parece –

+0

El único problema que veo con 'const' es que su preparación y * inteligibilidad * varía ampliamente según el nivel del programador de C++, (algo de confusión acerca de qué el elemento 'const' se aplica en una declaración compleja, por ejemplo). Además, es parte del lenguaje, y debe utilizarse, siempre que se haya leído el libro de B.Stroustrup :-) –

Respuesta

16

Las dos cuestiones principales que he visto quejas frecuentes acerca de los grupos de noticias, son

Creo que este último podría/debería ser compatible con el idioma.

Posiblemente junto con la compatibilidad con las implementaciones de funciones de miembros covariantes, ya que ambas necesitan algún modo de detectar el tipo del puntero this.

Un tercer problema es que

Saludos & HTH.,

+2

¿Qué API de Microsoft no son compatibles con const? IIRC, la API de Windows usa LPCTSTR, MFC usa const IIRC, etc. No se me ocurre ninguna. – Rup

+0

@Rup: necesitaría mucha investigación para todos. Estoy informando de qué se ha quejado la gente, pero puedo confirmarlo por mi propia experiencia. si hubiera solo algunas funciones problemáticas, tal vez recordaría nombres, pero todo ha terminado. –

+12

@Rup: muchas API de Microsoft no usan mucha const. ¿Cuándo fue la última vez que viste una const HANDLE? O un const IDirect3DDevice9 *? Microsoft usa const para cadenas y eso es todo. – Puppy

55

El único malo de const es que es seriamente subestimado por muchos desarrolladores. Es una de las mejores herramientas en la caja de herramientas de C++, muy nítida y, sin embargo, no es peligroso cortarse con ella.

+5

"Pero lo que hizo que los que se habían reído de mí dejaran de reírse fue cuando, después de varios días, se hizo evidente que algunos errores muy difíciles de reproducir que algunos de mis compañeros de trabajo estaban buscando desesperadamente habían desaparecido". (de [chat] (http://chat.stackoverflow.com/transcript/message/254713#254713)) –

1

"issues"?

Si no va a modificar el valor de un puntero pasado (se usa simplemente para la entrada de referencia de una función), marque que es const. Lo mismo si el valor de una variable en particular no cambiará después de su inicialización. Si una función es segura para invocar una instancia de clase const, márquela también en const. Cuantos más elementos estén correctamente anotados const, es menos probable que cometas un error inadvertidamente y cuantas más optimizaciones el compilador teóricamente pueda realizar en ausencia de conocimiento completo (como cuando compilas con solo prototipos de funciones disponibles).

Las versiones modernas de gcc tienen soporte para advertencias cuando intentas convertir una variable const en una no const. Sugiero que dejes esas advertencias habilitadas.

Lo único a lo que hay que tener cuidado es exactamente qué está marcando const; const char * foo() no es lo mismo que char * foo() const.

+0

No estoy preguntando cómo usar const. Ya sé cómo usar const. Estaba buscando * problemas * con const. – Puppy

3

El problema con const es que los programadores que utilizan de forma incorrecta nuestra manera inconsistente

-1

Un problema es que el idioma también le permite const_cast a la basura, lo que contradice el propósito de usar const en el primer lugar.

+0

en realidad no lo puedes descartar explícitamente y obviamente necesitas poder const_cast para admitir API heredadas –

+6

No es esto nuevo ... 'const' sería inútil ** sin ** la capacidad de descartarlo, porque en el código del mundo real, tiene que lidiar con las API escritas por programadores que no entienden la exactitud de la const (o en un tiempo en el que 'const' aún no formaba parte de C++). – fredoverflow

+0

@FredOverflow Ya veo ... Nunca antes me había encontrado con esa situación. Gracias por la aclaración. – Dave

17

Lo que falla con const es que muchos programadores no parecen ser capaces de entenderlo completamente, y un proyecto de "media const-correct" simplemente no funciona.Esto es lo que necesita saber:

  1. Foo vs const Foo (o Foo const)
  2. Foo& vs const Foo& (o Foo const&)
    • referencias-a-const se unen a todo tipo de cosas, mientras referencias a la no-const no lo hacen
  3. Foo* vs const Foo* (o Foo const*)
    • variables de puntero también pueden ser Foo* const y const Foo* const (o Foo const* const)
  4. void Foo::mutator() vs int Foo::accessor() const
    • pero los miembros del puntero dentro métodos constantes siguen apuntando a objetos no constante
    • para que podamos accidentalmente devuelve datos no const desde una función const
  5. iterator vs const_iterator
    • variables de iterador también pueden ser const iterator y const const_iterator

Migración a C++ de un lenguaje que no tiene un concepto const es bastante difícil, cualquier muchos no ven el punto.

+0

Nit-picking: las referencias a los objetos no pueden ser const, pero las referencias pueden hacer referencia a los objetos const. (La mayoría de la gente sabe a qué te refieres, pero la distinción es más importante con los punteros, donde tenemos tanto punteros como punteros para const.) – Philipp

+1

@Phil: Bien, cambié las partes relevantes de inglés a C++ ;-) – fredoverflow

+3

Esa cantidad los programadores no entienden que no es un problema con 'const'. Es un problema con los programadores y el estado gravemente patético de la educación de programación en todo el mundo occidental. –

49

El problema principal es que tiene que escribirlo. Debe ser el valor predeterminado, y todas las variables o parámetros variables deben especificarse explícitamente.

+6

No se puede votar lo suficiente. Aunque (o porque) causaría estragos en la mayoría de las ideas preconcebidas de los programadores. –

+2

Hooray para variables F # :) – fredoverflow

+4

Excepto por dos cosas: la mayoría de las variables se crean para escribirse. Hacer que todos lean solo de manera predeterminada crea más trabajo. También la filosofía del lenguaje C y C++ es que el programador sabe lo que está haciendo y que el lenguaje no debe impedirle hacer lo que quiera. – Jay

3

Una cosa que está "mal" es que no se puede convertir T ** a T const * const * que debe permitirse porque no es peligroso. No permitir que T ** se convierta a T const ** es correcto, esa conversión no es válida.

A veces he mencionado que const es en realidad una forma económica de "dividir" su interfaz en métodos de solo lectura y métodos de escritura. Probablemente sería poco práctico en C++. Sin embargo, sería más práctico en Java tener versiones de ReadOnly de colecciones, donde no tienen const y donde sus tipos de colección están más orientados a objetos.

const-ness no se propaga: El problema aquí es que si llevo mi clase, la constness no es "comprobada" por el compilador, es decir, mi clase de interfaz puede tener un método "const" llamar a un método non-const en el pImpl y el compilador no se quejará. Esto se debe a que lo único que mi método const está garantizado de no hacer es cambiar el puntero para apuntar a un objeto diferente, y mi pImpl nunca va a cambiar. Incluso podría ser un puntero const (no puntero a const).

La falta de adecuada covarianza entre shared_ptr<T> y shared_ptr<const T> podría ser un problema también, aunque en general he visto que no sea el problema, sino que los desarrolladores lo general typedef sus shared_ptrs y rara vez typedef la shared_ptr a const. Y a veces pasan const shared_ptr<T> & y piensan que están pasando un puntero compartido a una const T (como con const T *) que no lo son.

+0

"no se puede convertir' T ** 'a' T const * const * '" - esta parte es incorrecta (al menos con los compiladores que uso); recuerdo que fue una molestia en C pero C++ lo hizo bien. – anatolyg

+1

puede tener const-checking para la clase pimpl, no es gran cosa ... por ejemplo, observe el uso de Qts de las clases de pimpl ... (usan 2 funciones privadas de pimpl-accessor (const/nonconst) para acceder al objeto pimpl, si quiero asegurarme de que no accedas al objeto pimpl sin usar estas funciones (y así eludir la comprobación de const), puedes usar un 'void *' completamente opaco para el objeto pimpl, y agregar algunos reinterpret_casts a las funciones del acessor (mira la implementación de, por ejemplo, Q_DECLARE_PRIVATE) – smerlin

+2

@anatolyg: está en lo cierto. El primer párrafo no describe algo "incorrecto" con 'const' porque no es verdadero. Consulte 4.4 [conv.qual]/4 de ISO/IEC 14882: 2003. –

-2

Una cosa es que todavía es posible subvertirlo. es decirtodavía es legal para hacer algo como esto:

void foo(const inst& x) 
{ 
    const_cast<int&> x = 3; 
} 

Incluso puede usar cosas como memset subvertir sin una explícita const_cast.

Esto es una desventaja entre hacer que el compilador haga cumplir const y permitir cierta flexibilidad para las interfaces no conscientes de const.

Lo que conduce a otra limitación, ya que no se ha adoptado universalmente, lo cual se debe en parte a otro problema, que es que usar const es una proposición de todo o nada. Si comienza a usarlo, deberá propagarlo a través de su base de códigos.

+5

No, esto es posiblemente un Comportamiento no definido (si el objeto original se creó 'const') y en el contexto de esta función se debe suponer que es. Un' const_cast' es legal, pero se escribe en un objeto creado como 'const' , ya sea que hayas descartado la estafa stness o no, es ilegal. –

+0

Bueno, sí, pero si piensas como const en términos de un contrato o prometes que no lo cambiaré (incluso si antes no era const), entonces es posible romperlo. – JohnMcG

+0

Pero puede hacer promesas en forma de comentarios en todos los idiomas y luego subvertirlos. 'const', sin embargo, es una herramienta sin comentarios/integrada que te obliga a no romper el contrato _de manera predeterminada_, mientras que en lenguajes no const correctamente puedes cambiar algo documentado como invariante. –

0

Otro problema que no se ha mencionado aún es la posibilidad de una interfaz mal diseñada para subvertir const (incluso en ausencia de moldes).

Ejemplo:

class TreeNode { 
public: 
    TreeNode& getParent() const { return *parent_; } 
    TreeNode& getLeft() const { return *left_; } 
    TreeNode& getRight() const { return *right_; } 
private: 
    //TreeNode has a pointer to the Tree to enable navigation of the tree. 
    //Assume that other design constraints mean that this must be a pointer 
    //rather than a reference. 
    TreeNode* parent_; 
    TreeNode* left_; 
    TreeNode* right_; 
}; 
//This function demonstrates the ability for const to be subverted. 
TreeNode& remove_const(TreeNode const& toRemoveConstFrom) { 
    TreeNode& parent(toRemoveConstFrom.getParent()); 
    TreeNode& leftChild(parent.getLeft()); 
    TreeNode& rightChild(parent.getRight()); 
    return &toRemoveConstFrom == &leftChild ? leftChild : rightChild; 
} 

La naturaleza intransitivo de const significa que es posible tener una interfaz donde una referencia no const a un objeto puede obtenerse a partir de una referencia const a un objeto. Esto es algo de lo que hay que tener cuidado al diseñar interfaces.

0

La mayoría de las respuestas a continuación indican cosas como "lo que está mal con const es que X personas lo hacen Y". Esas no son respuestas sino síntomas. Esas no son cosas malas con const. Hay apenas cualquier cosa incorrecta con const ... Esas cosas están mal con las personas que no pueden RTFM.

0

const es genial. const es importante. const -corregir es condición necesaria para que una API sea buena.

Sin embargo, hay dos problemas que he tenido con const, repetidamente.

  • No hay manera de marcar una variable como const retroactivamente. Debe declarar un código variable, en cuyo caso tiene para inicializarlo inmediatamente. ¿Qué sucede si el código de inicialización contiene un if? Usted tiene la opción de omitir el const (indeseablemente), utilizando el operador ? en lugar de if (daños a la legibilidad). Java es así, las variables BTW - const no tienen que inicializarse de inmediato, solo tienen que inicializarse antes de se leen por primera vez, y deben inicializarse en todas las ramas de un if.

  • No hay forma de especificar que un objeto pasado por referencia a una función no cambiará mientras dure la llamada a la función. const T& t hace no significa que el objeto apuntado por t no cambiará, pero solo que la referencia t no se puede usar para cambiarlo. El compilador aún tiene que suponer que cualquier llamada de función que no vea en podría cambiar el objeto. En algunos casos, eso evita bastantes optimizaciones.