¿Cuáles son los problemas de llevar C ++ como const a un lenguaje?

17

Estoy interesado en la idea de C ++, como constno esa ejecución particular (como desechar const).

Tomemos, por ejemplo, C #: carece de C ++, como const, y la razón es la habitual: personas y tiempo. Aquí, además, parece que el equipo de C # analizó la ejecución de C ++ de constCLR de marketing y tuvo suficiente en este punto (vea por qué no hay un método de miembro const en C # y parámetro const ; gracias Svick). Si nos movemos más allá de eso, ¿hay algo más?

¿Hay algo más profundo? Tomemos, por ejemplo, la herencia múltiple: por lo general, se considera difícil de entender (para el usuario) y, por lo tanto, no se agrega al idioma (como el problema del diamante).

¿Hay algo en NATURE de constque representan un problema de que es mejor para el lenguaje para evitarlo? Algo así como decidir si constdebe ser profundo o poco profundo (tener constcontenedor significa que no puedo agregar un nuevo elemento o alterar elementos existentes también; ¿qué pasa si los elementos son del tipo de referencia)?

ACTUALIZACIÓN : si bien menciono C # y, por lo tanto, hago una perspectiva histórica, estoy interesado en la naturaleza de los posibles problemas del constlenguaje.

Este no es un desafío de los idiomas. Ignore factores como las tendencias actuales o la popularidad. Solo estoy interesado en cuestiones técnicas. Gracias.

Greenoldman
fuente
3
como un aparte: la herencia múltiple puede no ser difícil de entender, pero la resolución del método puede volverse bastante fea. La resolución ingenua de profundidad primero se vuelve rápidamente intuitiva (" problema de diamante "). El orden de resolución del método C3 (publicado '96) resuelve esto, pero ciertamente no es fácil de entender. Por lo tanto, prohibir la herencia múltiple a menudo puede parecer bastante sensato, pero el verdadero problema es que Java es anterior al algoritmo C3 y C # se orienta a sí mismo después de Java.
amon
3
@amon Mucha gente realmente no piensa que la herencia múltiple es una característica que desea tener en primer lugar, por ejemplo, los creadores del lenguaje de programación D: es una característica compleja de valor discutible. Es muy difícil de implementar de manera eficiente, y los compiladores son propensos a muchos errores al implementarlo. Casi todo el valor de MI se puede manejar con una sola herencia junto con interfaces y agregación. Lo que queda no justifica el peso de la implementación de MI. (fuente)
jcora
55
Duplicado de stackoverflow.com/q/4150478/41071 .
svick
2
Incluso sin la herencia múltiple, puede que ya codificar cualquier problema 3-SAT como la resolución de métodos (o más precisamente Resolución de sobrecarga) en C #, por lo que es NP-completo.
Jörg W Mittag
1
@svick, muchas gracias. Como no tenía ninguna respuesta para esta pregunta, me tomé la libertad de editarla en gran medida, para centrarme en la naturaleza del problema, no en las razones históricas, porque parece que el 99% de ellas fueron "tiempo + personas + anti C ++ sesgo + CLR ".
greenoldman

Respuestas:

10

Tenga en cuenta que si esto califica para usted, pero en lenguajes funcionales como Standard ML, todo es inmutable de manera predeterminada. La mutación es compatible a través de un reftipo de diferencia genérico . Entonces, una intvariable es inmutable, y una ref intvariable es un contenedor mutable para ints. Básicamente, las variables son variables reales en el sentido matemático (un valor desconocido pero fijo) y las refs son "variables" en el sentido de programación imperativo, una celda de memoria en la que se puede escribir y leer. (Me gusta llamarlos asignables ).

Creo que el problema constes doble. Primero, C ++ carece de recolección de basura, lo cual es necesario para tener estructuras de datos persistentes no triviales . const debe ser profundo para que tenga sentido, pero tener valores totalmente inmutables en C ++ no es práctico.

En segundo lugar, en C ++ debe optar por participar en constlugar de rechazarlo. Pero cuando se olvida de constalgo y luego lo arregla, terminará en la situación de "envenenamiento constante" mencionada en la respuesta de @ RobY, donde el constcambio caerá en cascada a lo largo del código. Si constfuera el predeterminado, no se encontraría aplicando constretroactivamente. Además, tener que agregar en consttodas partes agrega mucho ruido al código.

Sospecho que los lenguajes principales que siguieron (por ejemplo, Java) fueron fuertemente moldeados por el éxito y la forma de pensar de C y C ++. Caso en cuestión, incluso con la recolección de basura, la mayoría de las API de recolección de idiomas asumen estructuras de datos mutables. El hecho de que todo sea mutable y la inmutabilidad se vea como un caso de esquina habla mucho sobre la mentalidad imperativa detrás de los lenguajes populares.

EDITAR : Después de reflexionar sobre el comentario de Greenoldman, me di cuenta de que constno se trata directamente de la inmutabilidad de los datos; constcodifica en el tipo de método si tiene efectos secundarios en la instancia.

Es posible usar la mutación para lograr un comportamiento referencialmente transparente . Suponga que tiene una función que, cuando se llama sucesivamente, devuelve valores diferentes, por ejemplo, una función que lee un solo carácter stdin. Podríamos usar caché / memorizar los resultados de esta función para producir un flujo de valores referencialmente transparente. La secuencia sería una lista vinculada cuyos nodos llamarán a la función la primera vez que intente recuperar su valor, pero luego almacenar en caché el resultado. Entonces, si stdinconstains Hello, world!, la primera vez que intente recuperar el valor del primer nodo, leerá uno chary regresará H. Luego continuará regresando Hsin más llamadas para leer a char. Del mismo modo, el segundo nodo leería una chardestdinla primera vez que intente recuperar su valor, esta vez devuelve ey almacena en caché ese resultado.

Lo interesante aquí es que ha convertido un proceso inherentemente con estado en un objeto que aparentemente no tiene estado. Sin embargo, fue necesario mutar el estado interno del objeto (almacenando en caché los resultados) para lograr esto: la mutación fue un efecto benigno . Es imposible hacer nuestro CharStream consta pesar de que la transmisión se comporta como un valor inmutable. Ahora imagine que hay una Streaminterfaz con constmétodos y que todas sus funciones esperan const Streams. ¡No CharStreampuedes implementar la interfaz!

( EDITAR 2: Aparentemente hay una palabra clave de C ++ llamada mutableque nos permitiría hacer trampa y hacerCharStream const . Sin embargo, esta escapatoria destruye constlas garantías, ahora realmente no puedes estar seguro de que algo no mutará a través de sus constmétodos. Supongo que no es eso malo, ya que debe solicitar explícitamente la escapatoria, pero aún depende completamente del sistema de honor).

En segundo lugar, suponga que tiene funciones de orden superior, es decir, puede pasar funciones como argumentos a otras funciones. constness es parte de la firma de una función, por lo que no podrá pasar las no constfunciones como argumentos a las funciones que esperan constfunciones. Hacer cumplir ciegamente constaquí conduciría a una pérdida de generalidad.

Finalmente, manipular un constobjeto no garantiza que no esté mutando algún estado externo (estático o global) a sus espaldas, por lo que constlas garantías no son tan fuertes como parecen inicialmente.

Para mí no está claro que codificar la presencia o ausencia de efectos secundarios en el sistema de tipos sea universalmente una buena cosa.

Doval
fuente
1
+1 gracias. Los objetos inmutables son algo más que C ++ const (es más una vista). Pero digamos, C ++ tendría const por defecto, y varbajo demanda dejaría solo un problema: ¿la profundidad?
greenoldman
1
@greenoldman Buen punto. Ha pasado un tiempo desde que usé C ++, así que olvidé que puede tener una constreferencia a un no constobjeto. Aun así, no creo que tenga sentido tener una superficialidad const. El objetivo constes garantizar que no podrá modificar el objeto a través de esa referencia. No está permitido eludir constcreando una no constreferencia, entonces, ¿por qué estaría bien eludir constmutando los miembros del objeto?
Doval
+1 :-) Problemas muy interesantes en su actualización (viviendo con lambdas no / constantes y quizás un problema más débil con el estado externo), tengo que digerirlo por más tiempo. De todos modos, en cuanto a tu comentario, no tengo nada malo en mente, sino que estoy buscando características para aumentar la claridad del código (otro: punteros no nulos). Misma categoría aquí (al menos es mi propósito): defino func foo(const String &s)y esta es una señal sque no se cambiará foo.
greenoldman
1
@greenoldman Definitivamente puedo ver el atractivo y la justificación. Sin embargo, no creo que haya una solución real al problema que no sea evitar el estado mutable tanto como sea posible (junto con otras cosas desagradables como la nullherencia).
Doval
8

El principal problema es que los programadores tienden a no usarlo lo suficiente, por lo que cuando llegan a un lugar donde se requiere, o un responsable de mantenimiento más tarde quiere corregir la corrección constante, hay un gran efecto dominó. Todavía puede escribir código inmutable perfectamente bueno sin const, su compilador simplemente no lo hará cumplir, y tendrá más dificultades para optimizarlo. Algunas personas prefieren que su compilador no los ayude. No entiendo a esas personas, pero hay muchas.

En lenguajes funcionales, casi todo lo es const, y ser mutable es la rara excepción, si es que está permitido. Esto tiene varias ventajas, como una concurrencia más fácil y un razonamiento más fácil sobre el estado compartido, pero lleva un tiempo acostumbrarse si viene de un idioma con una cultura mutable. En otras palabras, no querer constes un problema de personas, no técnico.

Karl Bielefeldt
fuente
+1 gracias. Si bien el objeto inmutable es algo más de lo que estoy buscando (puede hacer que el objeto sea inmutable en C # o Java), incluso con un enfoque inmutable debe decidir si es profundo o poco profundo, tome por ejemplo el contenedor de punteros / referencias (¿puede cambiar el valor de los objetos a los que apunta). Después de un día, parece que el problema principal es más bien lo que está configurado como predeterminado, y es fácil de resolver al diseñar un nuevo lenguaje.
greenoldman
1
+1 para lolz @ "Algunas personas prefieren que su compilador no los ayude. No entiendo a esas personas, pero hay muchas".
Thomas Eding
6

Veo los inconvenientes como:

  • "envenenamiento const" es un problema cuando una declaración de const puede obligar a una declaración anterior o siguiente a usar const, y eso puede hacer que sus declaraciones anteriores o siguientes usen const, etc. Y si el envenenamiento const fluye a una clase en una biblioteca sobre el que no tienes control, entonces puedes quedarte atrapado en un mal lugar.

  • No es una garantía absoluta. Por lo general, hay casos en los que la constante solo protege la referencia, pero no puede proteger los valores subyacentes por una razón u otra. Incluso en C, hay casos extremos que const no puede alcanzar, lo que debilita la promesa que ofrece const.

  • en general, el sentimiento de la industria parece estar alejándose de las fuertes garantías en tiempo de compilación, por mucho consuelo que puedan brindarle a usted y a mí. Así que por la tarde pasé tocando el 66% de mi base de código debido al envenenamiento constante, algunos Python Guy ha lanzado 10 módulos Python perfectamente funcionales, bien diseñados, probados y totalmente funcionales. Y ni siquiera estaba pirateando cuando lo hizo.

Entonces, tal vez la pregunta sea, ¿por qué llevar a cabo una característica potencialmente controvertida que incluso algunos expertos son ambivalentes cuando intentas adoptar tu nuevo lenguaje elegante?

EDITAR: respuesta corta, un nuevo idioma tiene que subir una colina empinada para su adopción. Sus diseñadores realmente necesitan pensar mucho sobre qué características necesita para obtener la adopción. Take Go, por ejemplo. Google truncó brutalmente algunas de las batallas religiosas más profundas al hacer algunas elecciones de diseño de estilo Conan-the-Barbarian, y realmente creo que el lenguaje es mejor para eso, por lo que he visto.

Robar
fuente
2
Su segunda viñeta no es correcta. En C ++, hay tres / cuatro formas de declarar una referencia / puntero, wrt. constness: int *puntero mutable a valor mutable, int const *puntero mutable a valor int * constconstante , puntero constante a valor mutable, int const * constpuntero constante a valor constante.
phresnel
Gracias. Pregunté sobre la naturaleza, no sobre el marketing ("la industria se está alejando" no es una naturaleza similar a C ++ const), por lo tanto, tales "razones" considero irrelevantes (al menos para esta pregunta). Sobre el constenvenenamiento, ¿puedes dar un ejemplo? Porque AFAIK es la ventaja, pasas algo consty debe permanecer const.
greenoldman
1
No es el constproblema, sino cambiar el tipo realmente; hagas lo que hagas, siempre será un problema. Considere pasar RichString, y luego darse cuenta de que es mejor pasar String.
greenoldman
44
@RobY: No estoy seguro de a qué / a quién te diriges con este último y el anterior . Sin embargo: después de esa tarde recorriendo el código, debería haber aprendido que debería refactorizar la corrección de const de abajo hacia arriba, no de arriba hacia abajo, es decir, comenzar en las unidades más internas y avanzar hacia arriba. Quizás tus unidades tampoco estaban lo suficientemente aisladas, sino que estaban estrechamente acopladas, o has caído presa de los sistemas internos.
phresnel
77
El "envenenamiento constante" no es realmente un problema: el verdadero problema es que C ++ por defecto es no- const. Es demasiado fácil no hacer las constcosas que deberían ser constporque tienes que optar por él en lugar de salir de él.
Doval
0

Hay algunos problemas filosóficos al agregar constun idioma. Si declara consta una clase, eso significa que la clase no puede cambiar. Pero una clase es un conjunto de variables junto con métodos (o mutadores). Logramos la abstracción al ocultar el estado de una clase detrás de un conjunto de métodos. Si una clase está diseñada para ocultar el estado, entonces necesita mutadores para cambiar ese estado desde el exterior y ya no lo es const. Por lo tanto, solo es posible declarar consta las clases que son inmutables. Entonces, constparece posible solo en escenarios donde las clases (o los tipos que desea declarar const) son triviales ...

Entonces, ¿necesitamos de consttodos modos? No lo creo. Creo que puedes prescindir fácilmente constsi tienes un buen diseño. Qué datos necesita y dónde, qué métodos son métodos de datos a datos y qué abstracciones necesita para E / S, red, vista, etc. Luego, naturalmente, divide módulos y clases que se ocupan del estado y tiene métodos y métodos. clases inmutables que no necesitan estado.

Creo que la mayoría de los problemas surgen con objetos de datos grandes y gordos que pueden guardarse, transformarse y dibujarse y luego desea pasarlos a un renderizador, por ejemplo. Por lo tanto, no puede copiar el contenido de los datos, porque es demasiado costoso para un gran objeto de datos. Pero tampoco quieres que cambie, así que necesitas algo como constpiensas. Deje que el compilador descubra sus defectos de diseño. Sin embargo, si divide esos objetos solo en un objeto de datos inmutable e implementa los métodos en el módulo responsable, puede pasar (solo) el puntero y hacer las cosas que desea hacer con los datos al tiempo que garantiza la inmutabilidad.

Sé que en C ++ es posible declarar algunos métodos consty no declarar otros métodos, porque son mutadores. Y luego, un tiempo después, necesita un caché y luego un getter muta un objeto (porque se carga lentamente) y ya no lo es const. Pero ese era el objetivo de esta abstracción, ¿verdad? No sabes qué estado se muta con cada llamada.

Waster
fuente
1
"Por lo tanto, const parece posible solo en escenarios donde las clases (o los tipos que desea declarar const) son triviales ..." Las estructuras de datos persistentes son inmutables y no triviales. Simplemente no los verá en C ++ porque prácticamente todos comparten sus datos internos entre varias instancias, y C ++ carece de recolección de basura para que funcione. "Creo que puedes hacerlo fácilmente sin constante si tienes un buen diseño". Los valores inmutables hacen que el código sea mucho más fácil de razonar.
Doval
+1 gracias. ¿Podría por favor aclarar sobre "declarar const a una clase"? ¿Quiso decir establecer clase como una constante? En caso afirmativo, no estoy preguntando sobre esto: C ++ const funciona como una vista, puede declarar una const junto al objeto, no una clase (o algo cambiado en C ++ 11). También hay un problema con el siguiente párrafo y la palabra "fácilmente" - hay una buena pregunta sobre C ++ - constante en C # y la cantidad de trabajo que implica es primordial - para cada tipo que intente poner const(en sentido C ++ ) debe definir la interfaz, también para todas las propiedades.
greenoldman
1
Cuando estás hablando de una "clase" en realidad te refieres a "instancia de una clase", ¿verdad? Creo que es una distinción importante.
svick