O que C ++ faz melhor que D?


135

Eu aprendi recentemente D e estou começando a familiarizar-me com o idioma. Sei o que ele oferece, ainda não sei como usar tudo e não sei muito sobre idiomas D e assim por diante, mas estou aprendendo.

Eu gosto de D. É uma linguagem agradável, sendo, de certa forma, uma grande atualização para C, e bem feita. Nenhuma das características parece ter sido "trancada", mas na verdade muito bem pensada e bem projetada.

Você ouvirá frequentemente que D é o que C ++ deveria ter (deixo a questão de saber se isso é verdade ou não para cada um e para todos decidirem a si mesmos, a fim de evitar guerras desnecessárias). Também ouvi de vários programadores de C ++ que eles gostam de D muito mais que C ++.

Eu mesmo, enquanto conheço C, não posso dizer que conheço C ++. Eu gostaria de ouvir de alguém que conhece C ++ e D se acha que há algo que C ++ faz melhor que D como linguagem (o que significa que não é o habitual "ele tem mais bibliotecas de terceiros" ou "há mais recursos" ou " existem mais trabalhos que requerem C ++ do que D ").

O D foi projetado por alguns programadores de C ++ muito qualificados ( Walter Bright e Andrei Alexandrescu , com a ajuda da comunidade D) para corrigir muitos dos problemas do C ++, mas havia algo que realmente não melhorou afinal? Algo que ele perdeu? Algo que você acha que não era uma solução melhor?

Além disso, observe que estou falando de D 2.0 , não de D 1.0 .


15
Eu assegurei que a comunidade D visse isso, pois tenho certeza de que há muito mais desenvolvedores C ++ do que os desenvolvedores D por aqui. Dessa forma, você terá respostas mais interessantes ou pelo menos variadas.
30511 Klaim

7
Além disso, o D2 foi projetado por Walter Bright, mas também com Alexandrescu. Você pode corrigir isso na sua pergunta.
30511 Klaim

2
@Klaim: houve (e ainda existe) muito envolvimento da comunidade em D e na biblioteca padrão também.
Michal Minich 30/07

28
@Anto Como linguagem, o C ++ é muito melhor do que o D, fazendo você, o programador, odiar sua vida.
Arlen

6
@jokoon: Na verdade, sim, com muito pouco trabalho: digitalmars.com/d/2.0/interfaceToC.html
Anto

Respostas:


124

A maioria das coisas que C ++ "faz" melhor que D são meta coisas: C ++ possui melhores compiladores, ferramentas melhores, bibliotecas mais maduras, mais ligações, mais especialistas, mais tutoriais etc. Basicamente, possui mais e melhor de todas as coisas externas que você esperaria de uma linguagem mais madura. Isso é indiscutível.

Quanto à linguagem em si, existem algumas coisas que o C ++ faz melhor que o D na minha opinião. Provavelmente há mais, mas aqui estão algumas que eu posso listar em cima da minha cabeça:

O C ++ possui um sistema de tipos melhor pensado
Há alguns problemas com o sistema de tipos em D no momento, que parecem ser omissões no design. Por exemplo, atualmente é impossível copiar uma estrutura const para uma estrutura não-const se a estrutura contiver referências ou ponteiros de objetos de classe devido à transitividade de const e à maneira como os construtores de postblit trabalham nos tipos de valor. Andrei diz que sabe como resolver isso, mas não deu detalhes. O problema é certamente corrigível (a introdução de construtores de cópia no estilo C ++ seria uma correção), mas atualmente é um grande problema na linguagem.

Outro problema que me incomodou é a falta de const lógica (ou seja, não é mutablecomo no C ++). Isso é ótimo para escrever código seguro para threads, mas torna difícil (impossível?) Realizar uma intalação lenta dentro de objetos const (pense em uma função const 'get' que constrói e armazena em cache o valor retornado na primeira chamada).

Finalmente, dado estes problemas existentes, eu estou preocupado com a forma como o resto do sistema tipo ( pure, shared, etc.) irão interagir com tudo o resto na língua uma vez que eles são colocados em uso. Atualmente, a biblioteca padrão (Phobos) faz muito pouco uso do sistema de tipo avançado de D, então acho razoável a questão de saber se ela será mantida sob estresse. Sou cético, mas otimista.

Observe que o C ++ possui alguns tipos de verrugas do sistema (por exemplo, const não transitiva, exigindo iteratortambém const_iterator) que o tornam bastante feio, mas embora o sistema de tipos do C ++ esteja um pouco errado em algumas partes, ele não impede que você faça trabalhos como os de D às vezes faz.

Edit: Para esclarecer, acredito que o C ++ tenha um sistema de tipo melhor pensado - não necessariamente melhor - se isso fizer sentido. Essencialmente, em DI, há um risco envolvido no uso de todos os aspectos de seu sistema de tipos que não estão presentes no C ++.

Às vezes, D é um pouco conveniente demais.
Uma crítica que você costuma ouvir sobre o C ++ é que ele oculta alguns problemas de baixo nível, por exemplo, atribuições simples como a = b;fazer várias coisas como chamar operadores de conversão, chamar operadores de atribuição de sobrecarga etc. difícil de ver a partir do código. Algumas pessoas assim, outras não. De qualquer maneira, em D é pior (melhor?) Devido a coisas como opDispatch, @property, opApply, lazyque têm o potencial de alterar o código inocente em coisas que você não espera.

Eu não acho que isso seja um grande problema pessoalmente, mas alguns podem achar isso desanimador.

D requer coleta de lixo
Isso pode ser visto como controverso, porque é possível executar D sem o GC. No entanto, apenas porque é possível, não significa que seja prático. Sem um GC, você perde muitos recursos de D e o uso da biblioteca padrão seria como caminhar em um campo minado (quem sabe quais funções alocam memória?). Pessoalmente, acho totalmente impraticável usar D sem um GC, e se você não é fã de GCs (como eu sou), isso pode ser bastante desanimador.

Definições de matriz ingênua em D alocam memória
Esta é uma das minhas preocupações:

int[3] a = [1, 2, 3]; // in D, this allocates then copies
int a[3] = {1, 2, 3}; // in C++, this doesn't allocate

Aparentemente, para evitar a alocação em D, você deve fazer:

static const int[3] staticA = [1, 2, 3]; // in data segment
int[3] a = staticA; // non-allocating copy

Essas pequenas alocações "pelas costas" são bons exemplos dos meus dois pontos anteriores.

Editar: Observe que esse é um problema conhecido que está sendo trabalhado.
Edit: Agora está corrigido. Nenhuma alocação ocorre.

Conclusão
Concentrei-me nos negativos de D vs C ++ porque é isso que a pergunta fazia, mas não veja este post como uma declaração de que C ++ é melhor que D. Eu poderia facilmente fazer um post maior de lugares onde D é melhor que C ++. Cabe a você tomar a decisão de qual usar.


Eu olhei para D alguns anos atrás (antes da versão 2.0). A coleta de lixo não era realmente necessária na época - ela estava lá por padrão, mas você pode optar por excluir o código de baixo nível. O que pensei estar errado sobre isso é que não consegui encontrar uma maneira de voltar a entrar. Por exemplo, em um contêiner baseado em árvore, o código da biblioteca pode gerenciar a memória dos próprios nós da árvore. A árvore como um todo ainda pode ser colecionável, com o IIRC um destruidor que coleta todos esses nós. Mas os objetos referenciados pelos dados naquele contêiner também devem ser colecionáveis ​​- deve haver um gancho para marcar todos os itens de dados na árvore do GC.
Steve314

3
Você ainda pode desativar o GC para código de baixo nível - Peter está dizendo que o idioma atualmente depende muito dele. Além disso, você pode solicitar ao GC que verifique os intervalos fora de seu heap gerenciado usando sua API: GC.addRange do core.memory .
Vladimir Panteleev

+1 por apontar que a biblioteca padrão D é coletada de lixo e que o código GC-off não é uma interoperabilidade perfeita. Não é algo que eu pensei sobre isso, mas parece ser um grande obstáculo a ser superado.
masonk

132

Quando entrei para o desenvolvimento de D, eu estava na posição peculiar de ser uma das pessoas que mais sabem sobre C ++. Agora estou na posição ainda mais peculiar de ser também uma das pessoas que mais sabem sobre D. Não estou dizendo isso aos direitos de crédito ou de se gabar tanto quanto para observar que estou curiosamente posição privilegiada para responder a esta pergunta. O mesmo se aplica a Walter.

Em geral, perguntar o que C ++ (e com isso quero dizer C ++ 2011) é melhor que D é tão autocontraditório quanto a pergunta: "Se você paga um profissional para limpar sua casa, quais são os lugares que eles deixarão mais sujo do que antes? " Qualquer que tenha sido o valor que o C ++ poderia fazer, o D não pôde, sempre foi um problema para mim e Walter, então quase por definição não há nada que o C ++ possa fazer que não esteja ao alcance de D.

Uma coisa que raramente é entendida no design da linguagem (porque poucas pessoas têm a sorte de realmente fazer alguma) é que há muito menos erros não forçados do que parece. Muitos de nós, usuários de idiomas, examinamos uma construção ou outra e dizemos: "Eca! Isso é tão errado! O que eles estavam pensando?" O fato é que a maioria das instâncias embaraçosas de uma língua é consequência de algumas decisões fundamentais que são sólidas e desejáveis, mas que competem fundamentalmente ou se contradizem (por exemplo, modularidade e eficiência, concreção e controle, etc.).

Com tudo isso em mente, vou enumerar algumas coisas em que consigo pensar e, para cada uma, explicarei como a escolha de D deriva do desejo de cumprir outra carta de nível superior.

  1. D assume que todos os objetos são móveis por cópia bit a bit. Isso deixa uma minoria de projetos para o C ++, especificamente aqueles que usam ponteiros internos, ou seja, uma classe que contém ponteiros dentro de si. (Qualquer projeto desse tipo pode ser traduzido com custo de eficiência desprezível ou desprezível em D, mas haveria um esforço de tradução envolvido.) Tomamos essa decisão de simplificar bastante o idioma, tornar a cópia de objetos mais eficiente sem intervenção mínima ou do usuário e evitar o pântano inteiro da construção da cópia e as referências rvalue são exibidas por completo.

  2. D proíbe tipos de gênero ambíguo (que não podem decidir se são de valor ou de referência). Tais projetos são evitados por unanimidade em C ++ e quase sempre estão errados, mas alguns deles são tecnicamente corretos. Fizemos essa escolha porque ele não permite o código incorreto e apenas uma pequena fração do código correto que pode ser redesenhado. Acreditamos que é uma boa troca.

  3. D não permite hierarquias de várias raízes. Um pôster anterior aqui ficou muito empolgado com esse tópico em particular, mas esse é um terreno bem trilhado e não há vantagem palpável de hierarquias sem raiz sobre hierarquias que possuem uma raiz comum.

  4. Em D você não pode jogar, por exemplo, um int. Você deve lançar um objeto que herda Throwable. Não há dúvida de que o estado das coisas é melhor em D, mas, bem, é uma coisa que o C ++ pode fazer que D não pode.

  5. Em C ++, a unidade de encapsulamento é uma classe. Em D é um módulo (ou seja, arquivo). Walter tomou essa decisão por dois motivos: mapear naturalmente o encapsulamento para a semântica da proteção do sistema de arquivos e evitar a necessidade de "amigo". Essa opção se integra muito bem ao design de modularidade geral da D. Seria possível mudar as coisas para se parecerem com C ++, mas isso forçaria as coisas; As opções de escopo de encapsulamento do C ++ são boas apenas para o design físico do C ++.

Pode haver uma ou duas coisas menores, mas no geral deve ser isso.


6
@DeadMG: Para que isso funcione em C ++, o objeto que está sendo movido precisaria de um ponteiro de volta para o objeto que aponta para ele (para que ele pudesse ser atualizado durante a construção da cópia). Se for esse o caso, em D você pode usar o construtor postblit para atualizar o ponteiro de qualquer maneira. Por favor, não discuta contra D se você tiver apenas um conhecimento passageiro.
Peter Alexander

13
@ Peter: deve ser uma referência, mesmo que a vida seja estritamente baseada em escopo? Devo desperdiçar a sobrecarga de alocá-la dinamicamente e as despesas indiretas, de cache e de coleção, porque quero aliasá-la? Além disso, espero que o colecionador possa coletá-lo deterministicamente, para semântica equivalente. Isso claramente não está no controle.
DeadMG

3
@bi: A existência de uma classe superior não afeta suas escolhas. Na estrutura do tipo classe, sempre há um topo e um fundo. A parte inferior é explícita apenas em alguns idiomas . O topo (por exemplo, Objeto etc.) é explícito em mais idiomas. Esses tipos sempre existem no conceito; quando eles também estão acessíveis, eles simplesmente oferecem algumas facilidades extras ao usuário do idioma sem causar problemas.
Andrei Alexandrescu

6
@quant_dev: você ficará feliz em saber que já existe um projeto GSoC em boa forma, focado na álgebra linear de alto desempenho usando BLAS. Ele também fornece implementações "ingênuas" das primitivas apropriadas para fins de teste e benchmarking. Para responder à sua segunda pergunta, Java define um nível bastante baixo para comparar bibliotecas numéricas. Sempre terá o problema de ultrapassar uma barreira JNI para acessar bibliotecas de álgebra de alto desempenho, e a sintaxe será ruim porque o Java não possui sobrecarga do operador.
Andrei Alexandrescu

4
@ PeterAlexander: DeadMG está certo no ponto. "Você não deve usar ponteiros para tipos de valor" é claramente ignorante do fato de que ponteiros em qualquer idioma geralmente são usados ​​com tipos de valor (você realmente espera ver um Object*tão amplamente usado como um int*?) E D parece estar ignorando completamente a penalidade de desempenho ou alegando que não existe. Isso é obviamente falso - a falta de cache é bastante perceptível em muitos casos, portanto, o C ++ sempre terá essa vantagem de flexibilidade sobre D.
Mehrdad

65

Eu acho que você vai ter muita dificuldade em encontrar muito em D, que é objetivamentepior que C ++. A maioria dos problemas com D, nos quais você poderia objetivamente dizer que é pior, são problemas de qualidade de implementação (geralmente devido à rapidez com que a linguagem e a implementação são jovens e estão sendo corrigidos a uma velocidade vertiginosa ultimamente) ou são problemas com a falta de bibliotecas de terceiros (que virão com o tempo). A linguagem em si é geralmente melhor que C ++, e os casos em que C ++, como linguagem, é melhor geralmente são onde há uma troca em que C ++ foi por um lado e D foi por outro, ou onde alguém tem razões subjetivas para explicar o motivo. pense que um é melhor que outro. Mas o número de razões objetivas definitivas pelas quais o C ++, como linguagem, é melhor, provavelmente será muito pequeno.

Na verdade, tenho que arruinar meu cérebro para encontrar razões pelas quais C ++, como linguagem, é melhor que D. O que geralmente me vem à mente é uma questão de troca.

  1. Como a const de D é transitiva e porque a linguagem é imutável , ela tem garantias muito mais fortes do que as de C ++ const, o que significa que D não possui e não pode ter mutable. Não pode ter const lógico . Então, você obtém um grande ganho com o sistema const de D, mas em algumas situações, você simplesmente não pode usar constcomo usaria em C ++.

  2. D possui apenas um operador de conversão, enquanto C ++ possui 4 (5 se você contar o operador de conversão C). Isso torna mais fácil lidar com elencos em D em geral, mas é problemático quando você realmente deseja as complicações / benefícios extras que const_castseus irmãos fornecem. Mas D é realmente poderoso o suficiente para que você possa usar modelos para implementar os lançamentos de C ++; portanto, se você realmente os quiser, poderá obtê-los (e eles podem até acabar na biblioteca padrão de D em algum momento).

  3. D tem muito menos conversões implícitas que o C ++ e é muito mais provável que declare que duas funções estão em conflito entre si (forçando você a ser mais específico sobre qual das funções que você quer dizer - com conversões ou fornecendo o caminho completo do módulo ) Às vezes, isso pode ser irritante, mas evita todos os tipos de problemas de seqüestro de funções . Você sabe que está realmente chamando a função que pretende.

  4. O sistema de módulos de D é muito mais limpo do que o # C ++ inclui (para não mencionar, muito mais rápido na compilação), mas falta qualquer tipo de espaço de nome além dos próprios módulos. Portanto, se você deseja um espaço para nome em um módulo, é necessário seguir a rota Java e usar funções estáticas em uma classe ou estrutura. Funciona, mas se você realmente deseja namespacing, obviamente não é tão limpo quanto o namespacing real. Para a maioria das situações, no entanto, o espaço para nome que os próprios módulos fornecem a você é bastante (e bastante sofisticado quando se trata de coisas como conflitos, na verdade).

  5. Como Java e C #, D tem herança única em vez de herança múltipla, mas, diferentemente de Java e C #, oferece maneiras fantásticas de obter o mesmo efeito sem todos os problemas que a herança múltipla de C ++ possui (e a herança múltipla de C ++ pode ficar muito confusa) às vezes). D não apenas possui interfaces , mas também possui mixins de strings , mixins de modelo e alias . Portanto, o resultado final é sem dúvida mais poderoso e não possui todos os problemas que a herança múltipla de C ++.

  6. Semelhante ao C #, D separa estruturas e classes . Classes são tipos de referência que têm herança e são derivadas Object, enquanto estruturas são tipos de valor sem herança. Essa separação pode ser boa e ruim. Ele se livra do problema clássico de fatiar em C ++ e ajuda a separar tipos que são realmente de valor daqueles que deveriam ser polimórficos, mas, a princípio, pelo menos, a distinção pode ser irritante para um programador de C ++. Por fim, existem vários benefícios, mas obriga a lidar com seus tipos de maneira um pouco diferente.

  7. As funções de membro das classes são polimórficas por padrão. Você não pode declara-los não virtuais . Cabe ao compilador decidir se eles podem ser (o que realmente é o caso se forem finais e não estiverem substituindo uma função de uma classe base). Portanto, isso pode ser um problema de desempenho em alguns casos. No entanto, se você realmente não precisa do polimorfismo, tudo o que você precisa fazer é usar estruturas , e isso não é um problema.

  8. D tem um coletor de lixo embutido . Muitos do C ++ considerariam isso uma desvantagem séria e, verdade seja dita, no momento, sua implementação poderia usar algum trabalho sério. Ele está melhorando, mas definitivamente ainda não está a par do coletor de lixo do Java. No entanto, isso é atenuado por dois fatores. Primeiro, se você estiver usando principalmente estruturas e outros tipos de dados na pilha, não será um grande problema. Se o seu programa não estiver constantemente alocando e desalocando coisas no heap, tudo ficará bem. E dois, você pode pular o coletor de lixo se quiser e apenas usar C malloce free. Existem alguns recursos de idioma (como corte de matriz) com os quais você terá que evitar ou ter cuidado, e parte da biblioteca padrão não é realmente utilizável sem pelo menos algum uso do GC (particularmente o processamento de cadeias), mas você pode escrever em D sem usar o coletor de lixo se você realmente quer. A melhor coisa a fazer é provavelmente usá-lo em geral e evitá-lo onde a criação de perfil mostra que está causando problemas para o código crítico de desempenho, mas você pode evitá-lo completamente, se quiser. E a qualidade da implementação do GC melhorará com o tempo, eliminando muitas das preocupações que o uso de um GC pode causar. Portanto, em última análise, o GC não será um problema tão grande e, diferentemente do Java, você pode evitá-lo, se quiser.

Provavelmente existem outros também, mas é o que eu posso pensar no momento. E se você perceber, são todas compensações. D optou por fazer algumas coisas de maneira diferente do C ++, que têm vantagens definidas sobre como o C ++ faz isso, mas também têm algumas desvantagens. O que é melhor depende do que você está fazendo e, em muitos casos, provavelmente parecerá pior no começo e, em seguida, você não terá problemas com isso quando se acostumar. De qualquer forma, os problemas em D geralmente serão novos, causados ​​por coisas novas que outras linguagens não fizeram antes ou não da maneira que D fez. No geral, D aprendeu muito bem com os erros do C ++.

E D, como linguagem, melhora o C ++ de tantas maneiras que acho que geralmente é o caso de D ser objetivamente melhor.

  1. D tem compilação condicional . Esse é um dos recursos que frequentemente sinto falta quando estou programando em C ++. Se o C ++ o adicionasse, o C ++ melhoraria aos trancos e barrancos quando se trata de coisas como modelos.

  2. D tem reflexão em tempo de compilação .

  3. As variáveis ​​são locais de encadeamento por padrão, mas podem ser, sharedse você desejar. Isso torna o tratamento de threads muito mais limpo do que em C ++. Você está no controle completo. Você pode usar immutablee passagem de mensagens para comunicação entre threads, ou você pode fazer variáveis sharede fazê-lo o caminho C ++ com semáforos e variáveis de condição. Mesmo isso é aprimorado em C ++ com a introdução do sincronizado (semelhante ao C # e Java). Portanto, a situação de segmentação de D é muito melhor que a de C ++.

  4. Os modelos de D são muito mais poderosos que os modelos de C ++, permitindo que você faça muito mais, muito mais facilmente. E com a adição de restrições de modelo, as mensagens de erro são muito melhores do que em C ++. D torna os modelos muito poderosos e utilizáveis. Não é por acaso que o autor do Modern C ++ Design é um dos principais colaboradores de D. Acho que os modelos C ++ estão seriamente ausentes em comparação com os modelos D, e às vezes pode ser muito frustrante ao programar em C ++.

  5. D possui programação de contrato embutida .

  6. D tem uma estrutura de teste de unidade embutida .

  7. D possui suporte interno para unicode com string(UTF-8), wstring(UTF-16) e dstring(UTF-32). Torna mais fácil lidar com o unicode. E se você quiser apenas usar stringe geralmente não se preocupar com o unicode, poderá - embora alguma compreensão dos conceitos básicos do unicode ajude com algumas das funções padrão da biblioteca.

  8. A sobrecarga de operador de D é muito mais agradável que a de C ++, permitindo que você use uma função para sobrecarregar vários operadores ao mesmo tempo. Um excelente exemplo disso é quando você precisa sobrecarregar os operadores aritméticos básicos e suas implementações são idênticas, exceto para o operador. Os mixins de string facilitam a tarefa, permitindo que você tenha uma definição de função simples para todos eles.

  9. As matrizes de D são muito melhores que as matrizes de C ++. Eles não apenas são do tipo adequado com um comprimento, mas também podem ser anexados e redimensionados. Concatená-los é fácil. E o melhor de tudo, eles cortam . E isso é um grande benefício para o processamento de matriz eficiente. Strings são matrizes de caracteres em D, e não é um problema (na verdade, é ótimo!), Porque as matrizes de D são muito poderosas.

Eu poderia continuar e continuar. Muitas das melhorias que D fornece são pequenas coisas (como usar thispara nomes de construtores ou proibir declarações ou corpos de loop em que um ponto-e-vírgula é o corpo inteiro), mas algumas são bastante grandes e, quando você adiciona tudo, contribui para uma experiência de programação muito melhor. O C ++ 0x adiciona alguns dos recursos que D possui e que faltava em C ++ (por exemplo, autoe lambdas), mas mesmo com todas as suas melhorias, ainda não haverá muito que seja objetivamente melhor sobre C ++ como linguagem que D.

Não há dúvida de que existem muitas razões subjetivas para gostar uma da outra, e a imaturidade relativa da implementação de D pode ser um problema às vezes (embora tenha melhorado muito rapidamente ultimamente - especialmente desde que os repositórios foram movidos para o github ) , e a falta de bibliotecas de terceiros pode definitivamente ser um problema (embora o fato de D poder chamar facilmente funções C - e, em menor grau, funções C ++ - atenue definitivamente o problema). Mas esses são problemas de qualidade de implementação, e não problemas com o próprio idioma. E à medida que a qualidade dos problemas de implementação for corrigida, será muito mais agradável usar o D.

Portanto, suponho que a resposta curta a essa pergunta seja "muito pequena". D, como linguagem, geralmente é superior ao C ++.


2
As linguagens de coleta de lixo usam 2 a 5 vezes mais memória que os idiomas que não são da GC (de acordo com a palestra de Alexandrescu no YT), então esse é definitivamente um problema se esse (uso de memória) for o gargalo.
NoSenseEtAl

9

RAII e uso de memória da pilha

O D 2.0 não permite que o RAII aconteça na pilha porque removeu o valor da scopepalavra - chave na alocação de instâncias de classe na pilha.

Você não pode fazer herança de tipo de valor em D, tão efetivamente que o força a fazer uma alocação de heap para qualquer forma de RAII.
Ou seja, a menos que você use emplace, mas é muito doloroso de usar, pois você precisa alocar a memória manualmente. (Ainda não achei prático usá-lo emplaceem D.)


6

C ++ é muito melhor em forçar você a ser detalhado. Isso pode ser melhor ou pior aos seus olhos, dependendo se você gosta de inferência ou verbosidade.

Compare a memorização em tempo de execução no C ++ :

template <typename ReturnType, typename... Args>
function<ReturnType (Args...)> memoize(function<ReturnType (Args...)> func)
{
    map<tuple<Args...>, ReturnType> cache;
    return ([=](Args... args) mutable {
            tuple<Args...> t(args...);
            return cache.find(t) == cache.end()
                ? cache[t] : cache[t] = func(args...);
    });
}

com a mesma coisa em D:

auto memoize(F)(F func)
{
    alias ParameterTypeTuple!F Args;
    ReturnType!F[Tuple!Args] cache;
    return (Args args)
    {
        auto key = tuple(args);
        return key in cache ? cache[key] : (cache[key] = func(args));
    };
}

Observe, por exemplo, a verbosidade extra com template <typename ReturnType, typename... Args>versus (F), Args...versus Args, args...versus argsetc.
Para melhor ou pior, C ++ é mais detalhado.

Obviamente, você também pode fazer isso em D:

template memoize(Return, Args...)
{
    Return delegate(Args) memoize(Return delegate(Args) func)
    {
        Return[Tuple!Args] cache;
        return delegate(Args args)
        {
            auto key = tuple(args);
            return key in cache ? cache[key] : (cache[key] = func(args));
        };
    }
}

e pareceriam quase idênticos, mas isso exigiria a delegate, enquanto o original aceitava qualquer objeto que pudesse ser chamado. (A versão C ++ 0x requer umstd::function objeto, de qualquer forma, é mais detalhado e restritivo em suas entradas ... o que pode ser bom se você gosta de verbosidade e ruim se não.)


2

Eu não sei muito sobre D, mas muitos programadores de C ++ que eu conheço detestam bastante, e eu pessoalmente tenho que concordar - eu não gosto da aparência de D e não levarei mais perto.

Para entender por que D não está ganhando mais força, você precisa entender o que atrai as pessoas para C ++. Em uma palavra, a razão número um é o controle. Quando você programa em C ++, tem total controle sobre seu programa. Deseja substituir a biblioteca padrão? Você pode. Deseja fazer lançamentos de ponteiros inseguros? Você pode. Deseja violar a correção constante? Você pode. Deseja substituir o alocador de memória? Você pode. Deseja copiar a memória bruta sem considerar seu tipo? Se você realmente quer. Deseja herdar de várias implementações? É o seu funeral. Inferno, você pode até obter bibliotecas de coleta de lixo, como o coletor Boehm. Então você tem problemas como desempenho, que seguem de perto o controle - quanto mais controle um programador tiver, mais otimizado ele poderá criar seu programa.

Aqui estão algumas coisas que eu vi ao fazer uma pequena pesquisa e falar com algumas pessoas que tentaram:

Hierarquia de tipo unificado. Os usuários de C ++ usam herança muito raramente, a maioria dos programadores de C ++ prefere composição e os tipos só devem ser vinculados por herança se houver uma boa razão para fazê-lo. O conceito de objeto viola fortemente esse princípio, vinculando todos os tipos. Além disso, está violando um dos princípios mais básicos do C ++ - você só usa o que deseja. Não ter a opção de herdar do Object, e os custos que o acompanham, são muito fortes contra o que o C ++ representa como uma linguagem em termos de dar ao programador controle sobre seu programa.

Ouvi falar de problemas com funções e delegados. Aparentemente, D tem funções e delegados como tipos de função que podem ser chamados em tempo de execução, e eles não são os mesmos, mas são intercambiáveis ​​ou ... alguma coisa? Meu amigo teve alguns problemas com eles. Definitivamente, esse é um downgrade do C ++, que apenas possui std::functione pronto.

Então você tem compatibilidade. D não é particularmente compatível com C ++. Quero dizer, nenhuma linguagem é compatível com C ++, vamos ser sinceros, exceto C ++ / CLI, que é meio trapaceiro, mas como barreira à entrada, isso precisa ser mencionado.

Então, existem outras coisas. Por exemplo, basta ler a entrada da Wikipedia.

import std.metastrings;
pragma(msg, Format!("7! = %s", fact_7));
pragma(msg, Format!("9! = %s", fact_9));

printfé uma das funções mais inseguras já criadas, na mesma família de grandes problemas, como getsna antiga biblioteca C Standard. Se você procurá-lo no Stack Overflow, encontrará muitas e muitas perguntas relacionadas ao uso indevido. Fundamentalmente, printfé uma violação do DRY- você está dando o tipo na string de formato e, em seguida, novamente quando você argumenta. Uma violação do DRY onde, se você errar, coisas muito ruins acontecem - por exemplo, se você alterou um typedef de um inteiro de 16 bits para um de 32 bits. Também não é extensível - imagine o que aconteceria se todos inventassem seus próprios especificadores de formato. Os iostreams do C ++ podem ser lentos, e sua escolha de operador pode não ser a melhor, e sua interface pode funcionar, mas eles são fundamentalmente garantidos como seguros, e o DRY não é violado e pode ser facilmente estendido. Isso não é algo que se possa dizer printf.

Nenhuma herança múltipla. Essa não é a maneira C ++. Os programadores de C ++ esperam ter controle completo sobre seus programas e a linguagem que impõe o que você não pode herdar é uma violação desse princípio. Além disso, torna a herança (ainda mais) frágil, porque se você altera um tipo de uma interface para uma classe porque deseja fornecer uma implementação padrão ou algo assim, de repente todo o código do usuário é quebrado. Isso não é uma coisa boa.

Outro exemplo é stringe wstring. No C ++, já é bastante doloroso ter que converter entre eles, e essa biblioteca suporta Unicode, e essa antiga biblioteca C usa apenas e precisa const char*escrever versões diferentes da mesma função, dependendo do tipo de argumento de string que você deseja. Notavelmente, os cabeçalhos do Windows, por exemplo, possuem algumas macros extremamente irritantes para lidar com o problema que geralmente pode interferir no seu próprio código. A adição dstringà mistura só vai piorar as coisas, já que agora, em vez de dois tipos de string, você precisa gerenciar três. Ter mais de um tipo de string aumentará os problemas de manutenção e introduzirá códigos repetitivos que lidam com strings.

Scott Meyers escreve:

D é uma linguagem de programação criada para ajudar os programadores a enfrentar os desafios do desenvolvimento moderno de software. Isso é feito ao promover módulos interconectados por meio de interfaces precisas, uma federação de paradigmas de programação totalmente integrados, isolamento de encadeamento imposto por idioma, segurança de tipo modular, modelo de memória eficiente e muito mais.

O isolamento de encadeamento imposto pelo idioma não é um plus. Os programadores de C ++ esperam ter controle total sobre seus programas, e a linguagem que força alguma coisa definitivamente não é o que o médico ordenou.

Também vou mencionar a manipulação de strings em tempo de compilação. D tem a capacidade de interpretar o código D em tempo de compilação. Isto não é uma vantagem. Considere as enormes dores de cabeça causadas pelo pré-processador relativamente limitado de C, bem conhecido por todos os programadores veteranos de C ++, e então imagine o quanto esse recurso será mal utilizado. A capacidade de criar código D em tempo de compilação é excelente, mas deve ser semântica , não sintática.

Além disso, você pode esperar um certo reflexo. D tem coleta de lixo, que os programadores de C ++ associarão a linguagens como Java e C #, que são bastante diretamente opostas a ela nas filosofias, e as semelhanças sintáticas também as lembrarão. Isso não é necessariamente objetivamente justificável, mas é algo que certamente deve ser observado.

Fundamentalmente, ele não oferece tanto que os programadores de C ++ já não podem fazer. Talvez seja mais fácil escrever um metaprograma fatorial em D, mas já podemos escrever metaprogramas fatoriais em C ++. Talvez em D você possa escrever um traçador de raios em tempo de compilação, mas ninguém realmente quer fazer isso de qualquer maneira. Comparado às violações fundamentais da filosofia C ++, o que você pode fazer em D não é particularmente notável.

Mesmo que essas coisas sejam apenas problemas na superfície, tenho certeza de que o fato de que D na superfície não se parece com C ++ é provavelmente uma boa razão para que muitos programadores de C ++ não estejam migrando para D. Talvez D precise fazer um trabalho melhor anunciando a si mesmo.


9
@DeadMG: É 100% incorreto e falta entender o que dizer Este é definitivamente um downgrade do C ++, que apenas possui std::functione pronto. Por quê? Porque você também, por exemplo, possui ponteiros de função. É exatamente a mesma coisa nas "funções" de D: D são ponteiros de função, e os "delegados" de D são os mesmos que os de C ++ std::function(exceto que eles estão embutidos). Não há "downgrade" em nenhum lugar - e há uma correspondência 1: 1 entre eles, portanto não deve ser confuso se você estiver familiarizado com C ++.
Mehrdad

10
@ Mark Trapp: Devo admitir que não entendo muito bem sua posição sobre o assunto - os comentários não devem ser usados ​​para, você sabe, comentar uma resposta?
Klickverbot

6
@ Mark Trapp: Meu argumento é que a maioria dos comentários aqui não eram obsoletos (a meta-discussão que você vinculou se aplica especificamente a sugestões que já foram incorporadas ao post original), mas apontou imprecisões de fato no post, que ainda estão presentes .
Klickverbot

7
Uma observação sobre o formato: a função de formato de D é tipicamente segura (resolve os problemas de segurança / estouro) e não viola DRY, pois a sequência de formato especifica apenas como os argumentos devem ser formatados, e não seus tipos. Isso é possível graças às variáveis ​​de segurança tipográfica de D. Portanto, essa objeção é pelo menos completamente inválida.
Justin W

17
@ Mark: Qualquer que seja a política atual em relação a eles, acho estúpido e dificulta que as discussões sobre os comentários sejam excluídas . Acho que essa resposta teve discussões extensas (das quais agora estou interessado), mas não tenho certeza e não tenho como descobrir. Essa sala à qual você se vinculou tem mais de 10 mil mensagens e não tenho chance de encontrar uma discussão que me lembro de ter ocorrido, mas não consigo lembrar o conteúdo de. As discussões sobre esta resposta pertencem aqui, a essa resposta , e não a alguma sala de bate-papo, onde podem ser misturadas em discussões sobre sexo, drogas e rock'n'roll.
sbi 25/08

1

Uma coisa que eu aprecio em C ++ é a capacidade de documentar um argumento de função ou retornar um valor como uma referência C ++ em vez de um ponteiro, implicando, portanto, uma não- nullvalor.

Versão D:

class A { int i; }

int foo(A a) {
    return a.i; // Will crash if a is null
}

int main() {
    A bar = null;
    // Do something, forgetting to set bar in all
    // branches until finally ending up at:
    return foo(bar);
}

Versão C ++:

class A { int i; };

int foo(A& a) {
    return a.i; // Will probably not crash since
                // C++ references are less likely
                // to be null.
}

int main() {
    A* bar = null;
    // Do something, forgetting to set bar in all
    // branches until finally ending up at:
    // Hm.. I have to dereference the bar-pointer
    // here, otherwise it wont compile.  Lets add
    // a check for null before.
    if (bar)
        return foo(*bar);
    return 0;
}

Para ser justo, você pode se aproximar muito do C ++ transformando Aem D structe marcando o foo()-argumento como a ref(classes são tipos de referência e estruturas são tipos de valor em D, semelhante ao C #).

Acredito que exista um plano para criar um NonNullablemodelo para classes como uma construção de biblioteca padrão D. Mesmo assim, eu gosto da brevidade de apenas em Type&comparação com NonNullable(Type), e preferiria não anulável como o padrão (renderizando algo como Typee Nullable(Type)). Mas é tarde demais para mudar isso para D e estou me afastando do assunto agora.


3
Os argumentos da função e os valores de retorno em D podem ser marcados com refpara fornecer o mesmo efeito que os C ++ &. A única grande diferença é que refnão será temporário, mesmo que seja const.
Jonathan M Davis

Eu gosto da maneira como retornar referências a variáveis ​​locais é proibido em D, eu não sabia disso até ler seu comentário. Mas você ainda pode retornar uma referência nula não local sem pensar nela de uma maneira em que o operador de desreferência C faça você pensar.
Lum 2/08

Você está confundindo coisas. As classes são sempre referências, e isso é separado da ref. Referências em D são como referências em Java. Eles são ponteiros gerenciados. Passar ou retornar por ref é como passar ou retornar com & em C ++. Passar uma referência de classe por ref é como passar um ponteiro em C ++ com & (por exemplo, A * &). As aulas não ficam na pilha. Sim, NonNullable tornaria possível ter uma referência de classe que era garantida como não nula, mas que é completamente separada da ref. O que você está tentando fazer no código C ++ não funciona em D porque as classes não ficam na pilha. Estruturas fazem.
Jonathan M Davis

1
Então, sim, seria bom ter uma referência de classe que não seja nula, mas o C ++ consegue fazer o que você está mostrando, pois permite que as classes estejam na pilha e permite que os ponteiros sejam desferenciados. E enquanto você pode desreferenciar ponteiros em D, classes são referências, não ponteiros, portanto, você não pode desreferenciá-los. Como você não pode colocá-los na pilha nem desreferenciá-los, não há como incorporar D a uma classe que não possa ser nula. Ele é uma perda, mas nonnullable irá corrigi-lo, e os ganhos com a separação de estruturas e classes são geralmente maiores de qualquer maneira.
Jonathan M Davis

2
As referências C ++ não podem ser nulas pelo padrão da linguagem (dizendo "provavelmente não será nulo" está incorreto, pois não pode ser nulo). Eu gostaria que houvesse uma maneira de proibir nulo como um valor válido para uma classe.
Jsternberg #

1

A coisa mais importante que o C ++ "faz melhor" do que o D é a interface com as bibliotecas herdadas . Vários mecanismos 3D, OpenCL e similares. Como D é novo, ele tem uma quantidade muito menor de bibliotecas diferentes para escolher.

Outra diferença importante entre o C ++ e o D é que o C ++ tem vários fornecedores independentes financeiramente, mas a partir de 2014 o D tinha praticamente apenas um , a equipe de dois homens que o criou. É interessante que o "princípio da segunda fonte", que diz que os projetos nunca devem depender de tecnologia, componentes, que tenham apenas um único fornecedor, pareça válido até para o software.

Para comparação, a primeira versão do intérprete Ruby foi escrita pelo inventor de Ruby, o Yukihiro Matsumoto, mas o intérprete Ruby da era principal de 2014 foi escrito praticamente do zero por outras pessoas. Portanto, Ruby pode ser visto como uma linguagem que possui mais de um fornecedor financeiramente independente. D, por outro lado, pode ser uma tecnologia incrível, mas depende dos poucos desenvolvedores que a desenvolvem.

A história do Java mostra que, mesmo que uma tecnologia, neste caso, Java, tenha um financista fino, porém único, há um grande risco de que a tecnologia seja essencialmente descartada, independentemente da enorme base de usuários corporativos. Uma citação da Apache Software Foundation , onde a CE significa "Comitê Executivo":

A Oracle forneceu ao CE uma solicitação e licença de especificação Java SE 7 que são auto-contraditórias, restringem severamente a distribuição de implementações independentes da especificação e, o mais importante, proíbem a distribuição de implementações independentes de fonte aberta da especificação.

Como uma nota histórica, pode-se dizer que os applets Java aceleraram a tela 3D anos antes do desenvolvimento do HTML5 WebGL. O problema da velocidade de inicialização dos miniaplicativos Java poderia ter sido resolvido, se o Java tivesse sido um projeto da comunidade, mas os executivos do único financiador do Java, a Sun Microsystems, não consideraram importante o suficiente para corrigir a implementação do Java. O resultado final: a tela HTML5 de vários fornecedores como uma "réplica do pobre homem" das estruturas da GUI Java (Swing, etc.). Curiosamente, no lado do servidor, a linguagem de programação Python tem as mesmas vantagens que o Java prometeu: escreva uma vez, execute em todos os servidores, independentemente do hardware, desde que o aplicativo Python não seja compilado no código da máquina. O Python é quase tão antigo / jovem quanto o Java, mas, diferentemente do Java, ele '

Resumo:

Ao avaliar a tecnologia para uso em produção, as propriedades mais importantes das tecnologias são as pessoas que a desenvolvem, o processo social, onde o desenvolvimento ocorre e o número de equipes de desenvolvimento financeiramente independentes.

Se é mais vantajoso usar a tecnologia T1 em detrimento da tecnologia T2, depende dos fornecedores das tecnologias e se a tecnologia T1 permite resolver problemas relacionados ao projeto com menor custo que o T2. Por exemplo, se o problema do fornecedor único fosse ignorado, para os sistemas de informação o Java seria uma tecnologia "melhor" que o C ++, porque os binários Java não precisam de recompilação na implantação do novo hardware e a quantidade de trabalho de desenvolvimento de software relacionado ao gerenciamento de memória é menor para o Java do que para o C ++. Projetos desenvolvidos a partir do zero, por exemplo, projetos que não dependem de outras bibliotecas, podem ser mais baratos em D do que em C ++, mas, por outro lado, o C ++ tem mais de um fornecedor e, portanto, é menos arriscado a longo prazo. . (O exemplo de Java, onde o Sun Microsystems estava quase OK,

Uma possível solução alternativa para algumas das limitações do C ++

Uma das soluções possíveis para algumas das limitações do C ++ é usar um padrão de design, em que a tarefa inicial é dividida em partes e as partes são "classificadas" (classificadas, agrupadas, divididas, outras palavras bonitas para a mesma coisa) para 2 classes: controle tarefas relacionadas à lógica , em que os padrões de acesso à memória não permitem localidade; tarefas de processamento de sinal , onde a localidade é facilmente alcançada. As tarefas "semelhantes" ao processamento de sinal podem ser implementadas como um conjunto de programas de console relativamente simplistas. As tarefas relacionadas à lógica de controle são colocadas todas em um único script que é escrito em Ruby ou Python ou qualquer outra coisa, em que o conforto no desenvolvimento de software tem prioridade mais alta que a velocidade.

Para evitar a inicialização cara do processo do sistema operacional e a cópia de dados entre os processos do sistema operacional, o conjunto de aplicativos pequenos do console C ++ pode ser implementado como um único programa C ++ que é inicializado como um "servlet" pelo Ruby / Python / etc. roteiro. O Ruby / Python / etc. O script desliga o servlet antes da saída. Comunicação entre o "servlet" e o Ruby / Python / etc. O script ocorre sobre um pipe nomeado Linux ou algum mecanismo semelhante.

Se a tarefa inicial não se dividir facilmente em partes que podem ser classificadas para as duas classes mencionadas acima, uma coisa a tentar pode ser reformular o problema para que a tarefa inicial mude.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.