Quais são os problemas de trazer const - como C ++ para uma linguagem?

17

Estou interessado na ideia de C ++ - constnão como aquela execução específica (como jogar fora const).

Tomemos, por exemplo, C # - falta const, como C ++, e o motivo é o habitual - pessoas e tempo. Aqui, além disso, parece que a equipe do C # analisou a execução do C ++ no constmarketing de CLR e já tinha o suficiente neste momento (veja por que não há um método membro const no parâmetro c # e const ; obrigado Svick). Seja se formos mais longe do que isso, há mais alguma coisa?

Existe algo mais profundo? Tomemos, por exemplo, a herança múltipla - geralmente é vista como difícil de entender (para o usuário) e, portanto, não é adicionada ao idioma (como o problema do diamante).

Há algo em NATUREZA do constque representar um problema que é melhor para a linguagem para evitá-lo? Algo como decidir se constdeve ser profundo ou superficial (ter constcontêiner significa que não posso adicionar novos elementos nem alterar elementos existentes; e se os elementos forem do tipo de referência)?

ATUALIZAÇÃO : enquanto menciono C #, e, portanto, criando uma perspectiva histórica, estou interessado na natureza dos possíveis problemas do constidioma.

Este não é um desafio das línguas. Ignore fatores como tendências atuais ou popularidade - só estou interessado em questões técnicas - obrigado.

greenoldman
fonte
3
como um aparte: a herança múltipla pode não ser difícil de entender, mas a resolução do método pode se tornar bastante feia. A resolução ingênua de profundidade em primeiro lugar se torna pouco intuitiva (" problema do diamante "). A ordem de resolução do método C3 (publicada em 96) resolve isso, mas certamente não é fácil de entender. Portanto, proibir a herança múltipla geralmente pode parecer bastante sensato - mas o problema real é que o Java antecede o algoritmo C3 e o C # se orientou após o Java.
amon
3
@amon Muitas pessoas realmente não pensam que a herança múltipla é um recurso que você deseja ter em primeiro lugar, por exemplo, os criadores da programação D defendem: É um recurso complexo de valor discutível. É muito difícil de implementar de maneira eficiente, e os compiladores são propensos a muitos erros na implementação. Quase todo o valor do MI pode ser tratado com herança única, juntamente com interfaces e agregação. O que resta não justifica o peso da implementação do MI. (fonte)
jcora 4/14
5
Duplicar de stackoverflow.com/q/4150478/41071 .
precisa
2
Mesmo sem a herança múltipla, você pode codificar qualquer problema 3-SAT como Resolução do método (ou mais precisamente a Resolução de sobrecarga) em C #, tornando-o NP completo.
Jörg W Mittag
1
@ Rick, muito obrigado. Como não tinha respostas para essa pergunta, tomei a liberdade de editá-la intensamente, para me concentrar na natureza do problema, não nas razões históricas, porque parece que 99% delas era "tempo + pessoas + viés anti-C ++ + CLR ".
greenoldman

Respostas:

10

Observe se isso se qualifica para você, mas em idiomas funcionais como o ML padrão tudo é imutável por padrão. A mutação é suportada por meio de um reftipo de essência genérico . Portanto, uma intvariável é imutável e uma ref intvariável é um contêiner mutável para ints. Basicamente, variáveis ​​são variáveis reais no sentido matemático (um valor desconhecido, mas fixo) refes são "variáveis" no sentido imperativo da programação - uma célula de memória que pode ser escrita e lida. (Gosto de chamá-los de atribuíveis .)

Eu acho que o problema consté duplo. Primeiro, o C ++ não possui coleta de lixo, o que é necessário para ter estruturas de dados persistentes não triviais . const deve ser profundo para fazer algum sentido, mas ter valores totalmente imutáveis ​​em C ++ é impraticável.

Segundo, em C ++ você precisa optar por constnão optar por sair dele. Mas quando você se esquece de constalgo e depois o corrige, você acabará na situação "const envenenamento" mencionada na resposta do @ RobY, onde a constmudança ocorrerá em cascata no código. Se constfosse o padrão, você não se aplicaria constretroativamente. Além disso, ter que adicionar em constqualquer lugar adiciona muito ruído ao código.

Eu suspeito que as principais linguagens que se seguiram (por exemplo, Java) foram fortemente moldadas pelo sucesso e pela maneira de pensar de C e C ++. Caso em questão, mesmo com a coleta de lixo, a maioria das APIs de coleta de idiomas assume estruturas de dados mutáveis. O fato de tudo ser mutável e a imutabilidade ser visto como um caso de canto fala muito sobre a mentalidade imperativa por trás das línguas populares.

EDIT : Depois de refletir sobre o comentário de greenoldman, percebi que constnão se trata diretamente da imutabilidade dos dados; constcodifica no tipo do método se tem efeitos colaterais na instância.

É possível usar a mutação para obter um comportamento referencialmente transparente . Suponha que você tenha uma função que, quando chamada, retorne sucessivamente valores diferentes - por exemplo, uma função que lê um único caractere stdin. Poderíamos usar cache / memorizar os resultados dessa função para produzir um fluxo de valores referencialmente transparente. O fluxo seria uma lista vinculada cujos nós chamarão a função na primeira vez que você tentar recuperar seu valor, mas depois armazenará em cache o resultado. Portanto, se stdinconsiderar Hello, world!, na primeira vez que você tentar recuperar o valor do primeiro nó, ele lerá um chare retornará H. Depois disso, ele continuará retornando Hsem mais chamadas para ler a char. Da mesma forma, o segundo nó leria um chardestdina primeira vez que você tenta recuperar seu valor, dessa vez retornando ee armazenando em cache esse resultado.

O interessante aqui é que você transformou um processo inerentemente estável em um objeto aparentemente sem estado. No entanto, foi necessário alterar o estado interno do objeto (armazenando os resultados em cache) para conseguir isso - a mutação foi um efeito benigno . É impossível fazer o nosso, CharStream constmesmo que o fluxo se comporte como um valor imutável. Agora imagine que há uma Streaminterface com constmétodos, e todas as suas funções esperam const Streams. Você CharStreamnão pode implementar a interface!

( EDIT 2: Aparentemente, existe uma palavra-chave C ++ chamada mutableque nos permite trapacear e criarCharStream const . No entanto, essa brecha destrói constas garantias - agora você realmente não pode ter certeza de que algo não sofrerá alterações por meio de seus constmétodos. Suponho que não seja isso. ruim, pois você deve solicitar explicitamente a brecha, mas ainda depende totalmente do sistema de honra.)

Em segundo lugar, suponha que você tenha funções de alta ordem - ou seja, você pode passar funções como argumentos para outras funções. constness faz parte da assinatura de uma função, portanto você não seria capaz de passar não- constfunções como argumentos para funções que esperam constfunções. Aplicar cegamente constaqui levaria a uma perda de generalidade.

Finalmente, manipular um constobjeto não garante que ele não esteja alterando algum estado externo (estático ou global) pelas suas costas, portanto constas garantias não são tão fortes quanto parecem inicialmente.

Não está claro para mim que codificar a presença ou ausência de efeitos colaterais no sistema de tipos é universalmente uma coisa boa.

Doval
fonte
1
+1 obrigado. Objetos imutáveis ​​são algo mais que C ++ const (é mais uma visão). Mas digamos, C ++ teria const por padrão e varsob demanda deixaria apenas um problema - a profundidade?
greenoldman
1
@greenoldman Bom ponto. Já faz um tempo desde que eu usei C ++, então esqueci que você pode ter uma constreferência a um não- constobjeto. Mesmo assim, não acredito que faça sentido ter uma superficialidade const. O ponto principal consté a garantia de que você não poderá modificar o objeto por meio dessa referência. Você não tem permissão para contornar constcriando uma não constreferência, então por que seria correto contornar constalterando os membros do objeto?
Doval
+1 :-) Questões muito interessantes em sua atualização (vivendo com non / const lambdas e talvez com problemas mais fracos com o estado externo), tenho que digeri-lo por mais tempo. De qualquer forma, quanto ao seu comentário, não tenho nada de mal em mente - pelo contrário, estou procurando recursos para aumentar a clareza do código (outro: ponteiros não nulos). Mesma categoria aqui (pelo menos é o meu objetivo) - eu defino func foo(const String &s)e este é um sinal sque não será alterado foo.
greenoldman
1
@ Greenreenman Eu definitivamente posso ver o apelo e a justificativa. No entanto, eu não acho que há uma solução real para o problema que não seja evitando estado mutável, tanto quanto possível (junto com outras coisas desagradáveis como nulle herança.)
Doval
8

O principal problema é que os programadores tendem a não usá-lo o suficiente, então, quando atingem um local onde é necessário, ou um mantenedor mais tarde deseja corrigir a correção constante, há um enorme efeito cascata. Você ainda pode escrever um código imutável perfeitamente bom sem const, seu compilador simplesmente não o aplicará e terá mais dificuldade em otimizá-lo. Algumas pessoas preferem que seu compilador não as ajude. Eu não entendo essas pessoas, mas existem muitas.

Nas linguagens funcionais, praticamente tudo é const, e ser mutável é a rara exceção, se é permitido. Isso tem várias vantagens, como concorrência mais fácil e raciocínio mais fácil sobre o estado compartilhado, mas leva algum tempo para se acostumar se você vem de um idioma com uma cultura mutável. Em outras palavras, não querer consté uma questão de pessoas, não técnica.

Karl Bielefeldt
fonte
+1 obrigado. Embora objeto imutável seja outra coisa que eu estou procurando (você pode tornar um objeto imutável em C # ou Java), mesmo com uma abordagem imutável, você deve decidir se é profundo ou raso, por exemplo, o contêiner de ponteiros / referências (você pode altere o valor dos objetos que estão sendo apontados). Depois de um dia, parece que o principal problema é o que é definido como padrão e é facilmente solucionável ao criar um novo idioma.
greenoldman
1
+1 para o lolz @ "Algumas pessoas preferem que o compilador não as ajude. Eu não entendo essas pessoas, mas existem muitas."
9788 Thomas Eding
6

Vejo os inconvenientes como:

  • "envenenamento const" é um problema quando uma declaração de const pode forçar uma declaração anterior ou seguinte a usar const, e isso pode fazer com que suas declarações anteriores a seguir usem const, etc. E se o envenenamento const fluir para uma classe em uma biblioteca que você não tem controle, pode ficar preso em um lugar ruim.

  • não é uma garantia absoluta. Geralmente, há casos em que o const apenas protege a referência, mas é incapaz de proteger os valores subjacentes por um motivo ou outro. Mesmo em C, existem casos extremos que a const não pode alcançar, o que enfraquece a promessa que a const oferece.

  • em geral, o sentimento da indústria parece estar se afastando de fortes garantias em tempo de compilação, por mais conforto que elas possam trazer a você e a mim. O cara lançou 10 módulos Python perfeitamente funcionais, bem projetados, bem testados e totalmente funcionais. E ele nem estava hackeando quando fez isso.

Então, talvez a pergunta seja: por que levar adiante um recurso potencialmente controverso sobre o qual até alguns especialistas são ambivalentes quando você está tentando obter a adoção de seu novo e sofisticado idioma?

EDIT: resposta curta, um novo idioma tem uma colina íngreme para subir para adoção. Os designers realmente precisam pensar muito sobre os recursos necessários para obter adoção. Pegue o Go, por exemplo. O Google meio que brutalmente truncou algumas das mais profundas batalhas religiosas ao fazer algumas escolhas de design ao estilo Conan-Bárbaro, e acho que a linguagem é melhor para ela, pelo que vi.

Roubar
fonte
2
Seu segundo marcador não está correto. No C ++, existem três / quatro maneiras de declarar uma referência / ponteiro, wrt. constness: int *ponteiro mutável para valor mutável, int const *ponteiro mutável para valor int * constconst , ponteiro const para valor mutável, int const * constponteiro const para valor const.
Phreelhor
Obrigado. Eu perguntei sobre a natureza, não sobre o marketing ("indústria que se afasta" não é do tipo C ++ const), portanto, essas "razões" considero irrelevantes (pelo menos para essa pergunta). Sobre constenvenenamento - você pode dar um exemplo? Porque AFAIK é a vantagem, você passa algo conste deve permanecer const.
greenoldman
1
Não é o constproblema, mas mudar realmente o tipo - faça o que fizer, sempre será um problema. Considere passar RichStringe depois perceber que é melhor passar String.
greenoldman
4
@RobY: Eu não sou certo o que / quem você está dirigindo com este último e ex . No entanto: depois daquela tarde, percorrendo o código, você deveria ter aprendido que deveria refatorar a correção constante de baixo para cima, não de cima para baixo, ou seja, comece nas unidades mais internas e suba. Talvez suas unidades também não estivessem isoladas o suficiente, em vez de estarem fortemente acopladas, ou você tenha sido vítima de sistemas internos.
Phresnel
7
"Envenenamento Const" não é realmente um problema - o verdadeiro problema é que o C ++ é padronizado como não const. É muito fácil ignorar as constcoisas que deveriam ser, constporque você precisa optar por isso em vez de desistir.
Doval
0

Existem alguns problemas filosóficos em adicionar consta um idioma. Se você declarar constpara uma classe, isso significa que a classe não pode mudar. Mas uma classe é um conjunto de variáveis ​​acopladas a métodos (ou mutadores). Atingimos a abstração ocultando o estado de uma classe atrás de um conjunto de métodos. Se uma classe é projetada para ocultar o estado, é necessário que os mutadores mudem esse estado de fora e, então, não é constmais. Portanto, só é possível declarar constpara classes que são imutáveis. Portanto, constparece apenas possível em cenários em que as classes (ou os tipos que você deseja declarar const) são triviais ...

Então, precisamos constmesmo assim? Acho que não. Eu acho que você pode facilmente constficar sem se tiver um bom design. Quais dados você precisa e onde, quais métodos são métodos de dados a dados e quais abstrações você precisa para E / S, rede, exibição etc. Então você naturalmente divide módulos e classes que lidam com estado e você tem métodos e métodos. classes imutáveis ​​que não precisam de estado.

Eu acho que a maioria dos problemas surge com objetos de dados grandes e gordurosos que podem salvar, transformar e desenhar a si mesmos, e então você deseja passá-los para um renderizador, por exemplo. Portanto, você não pode copiar o conteúdo dos dados, porque isso é muito caro para um grande objeto de dados. Mas você também não quer que isso mude, então precisa de algo como constpensa. Deixe o compilador descobrir suas falhas de design. No entanto, se você dividir esses objetos em apenas um objeto de dados imutável e implementar os métodos no módulo responsável, poderá distribuir (apenas) o ponteiro e fazer o que deseja com os dados, além de garantir a imutabilidade.

Eu sei que é possível em C ++ declarar alguns métodos conste não declarar em outros métodos, porque eles são mutadores. E, algum tempo depois, você precisa de um cache e, em seguida, um getter modifica um objeto (porque ele carrega lentamente) e não é constmais. Mas esse era o objetivo dessa abstração, certo? Você não sabe qual estado é alterado a cada chamada.

Waster
fonte
1
"Portanto, const parece apenas possível em cenários em que as classes (ou os tipos que você deseja declarar const) são triviais ..." As estruturas de dados persistentes são imutáveis ​​e não triviais. Você simplesmente não os verá no C ++ porque praticamente todos compartilham seus dados internos entre várias instâncias, e o C ++ não possui coleta de lixo para fazer esse trabalho. "Eu acho que você pode facilmente passar sem const se tiver um bom design." Valores imutáveis ​​tornam o código muito mais simples de se raciocinar.
Doval
+1 obrigado. Você poderia esclarecer sobre "declarar const para uma classe"? Você quis dizer definir classe como uma const? Se sim, não estou perguntando sobre isso - o C ++ const funciona como uma exibição, você pode declarar um const ao lado do objeto, não uma classe (ou algo alterado no C ++ 11). Também existe um problema com o próximo parágrafo e a palavra "facilmente" - há uma boa pergunta sobre C ++ - const em C # e a quantidade de trabalho envolvida é primordial - para todo tipo que você pretende colocar const(no sentido de C ++ ) você deve definir a interface, também para todas as propriedades.
greenoldman
1
Quando você está falando de uma "classe", na verdade você quer dizer "instância de uma classe", certo? Eu acho que é uma distinção importante.
svick