Qu'est-ce que C ++ fait mieux que D?

135

J'ai récemment appris le D et je commence à me familiariser avec la langue. Je sais ce que ça offre, je ne sais pas encore tout utiliser, et je ne connais pas grand chose au sujet des idiomes D, etc., mais j'apprends.

J'aime D. C'est un bon langage, étant, d'une certaine manière, une énorme mise à jour de C, et bien fait. Aucune de ces caractéristiques ne semble être "intégrée", mais en réalité assez bien pensée et bien conçue.

Vous entendrez souvent dire que D est ce que C ++ aurait dû être (je laisse la question de savoir si chacun doit décider lui-même afin d'éviter des guerres de flammes inutiles). J'ai également entendu plusieurs programmeurs C ++ affirmer qu'ils appréciaient beaucoup plus le C ++.

Moi-même, alors que je connais le C, je ne peux pas dire que je connais le C ++. J'aimerais entendre quelqu'un qui connaît à la fois C ++ et D s'il pense qu'il y a quelque chose que C ++ fait mieux que D en tant que langage (c'est-à-dire pas l'habituel "il a plus de bibliothèques tierces" ou "il y a plus de ressources" ou " plus de travaux nécessitant C ++ que D existe ").

D a été conçu par des programmeurs C ++ très expérimentés ( Walter Bright et Andrei Alexandrescu , avec l’aide de la communauté D) pour résoudre de nombreux problèmes du C ++, mais y avait-il quelque chose qui ne s’est pas amélioré après tout? Quelque chose lui a manqué? Quelque chose que tu penses n'était pas une meilleure solution?

Notez également que je parle de D 2.0 et non de D 1.0 .

Anto
la source
15
Je me suis assuré que la communauté D le voit bien car je suis sûr qu'il y a beaucoup plus de développeurs C ++ que de développeurs D ici. De cette façon, vous obtiendrez des réponses plus intéressantes ou du moins variées.
Klaim
7
En outre, D2 a été conçu par Walter Bright mais aussi avec Alexandrescu. Vous voudrez peut-être résoudre ce problème dans votre question.
Klaim
2
@Klaim: Il y avait (et il y a toujours) beaucoup d'implication de la communauté dans les bibliothèques D et standard.
Michal Minich
28
@Anto En tant que langage, C ++ est bien meilleur que D pour vous faire, le programmeur, détester votre vie.
Arlen
6
@jokoon: En fait, oui, avec très peu de travail: digitalmars.com/d/2.0/interfaceToC.html
Anto

Réponses:

124

La plupart des choses que C ++ "fait" mieux que D sont méta choses: C ++ a de meilleurs compilateurs, de meilleurs outils, des bibliothèques plus matures, plus de liaisons, plus d'experts, plus de tutoriels, etc. Fondamentalement, il a plus et mieux attendrait d'un langage plus mature. C'est indiscutable.

En ce qui concerne le langage lui-même, il y a quelques choses que C ++ fait mieux que D à mon avis. Il y a probablement plus, mais en voici quelques-unes que je peux en citer:

C ++ a un système de types mieux pensé
Il existe actuellement quelques problèmes avec le système de types en D, qui semblent être des oublis dans la conception. Par exemple, il est actuellement impossible de copier une structure const dans une structure non-const si celle-ci contient des références à des objets de classe ou des pointeurs en raison de la transitivité de const et de la manière dont les constructeurs postblit travaillent sur les types valeur. Andrei dit qu'il sait comment résoudre ce problème, mais n'a donné aucun détail. Le problème est certainement réparable (introduire des constructeurs de copie de style C ++ serait un correctif), mais c’est un problème majeur de langage à l’heure actuelle.

Un autre problème qui m'a posé problème est le manque de const logique (c'est-à-dire pas mutablecomme en C ++). C'est très bien pour écrire du code thread-safe, mais il est difficile (impossible?) De faire une initialisation lente dans les objets const (pensez à une fonction const 'get' qui construit et met en cache la valeur renvoyée lors du premier appel).

Enfin, compte tenu de ces problèmes existants, je suis inquiet sur la façon dont le reste du système de type ( pure, shared, etc.) va interagir avec tout le reste dans la langue une fois qu'ils sont mis à contribution . La bibliothèque standard (Phobos) utilise actuellement très peu le système de types avancé de D, je pense donc qu'il est raisonnable de se demander si elle résistera au stress. Je suis sceptique mais optimiste.

Notez que C ++ a des verrous de système de types (par exemple, const non transitive, nécessitant iteratoraussi bien que const_iterator) qui le rendent assez laid, mais si le système de types de C ++ est un peu faux à certains endroits, cela ne vous empêche pas de faire un travail comme D fait parfois.

Edit: Pour clarifier, je pense que C ++ a un système de types mieux pensé - pas nécessairement meilleur - si cela a du sens. En DI, nous estimons qu’il est risqué d’utiliser tous les aspects de son système de types qui ne sont pas présents en C ++.

D est parfois un peu trop pratique
Une des critiques que vous entendez souvent à propos du C ++ est qu’il vous cache certains problèmes de bas niveau, par exemple, de simples tâches comme a = b;faire des opérateurs comme des opérateurs de conversion, des opérateurs d’attribution de surcharge, etc. difficile à voir à partir du code. Certaines personnes aiment ça, d'autres non. De toute façon, en D , il est pire (mieux?) À cause de choses comme opDispatch, @property, opApply, lazyqui ont le potentiel de changer le code air innocent dans les choses que vous ne penserez pas.

Je ne pense pas que ce soit un gros problème personnellement, mais certains pourraient trouver cela déconcertant.

D nécessite le ramassage des ordures.
Cela peut être considéré comme controversé car il est possible de lancer D sans le GC. Cependant, ce n'est pas parce que c'est possible que c'est pratique. Sans GC, vous perdez beaucoup de fonctionnalités de D, et utiliser la bibliothèque standard équivaudrait à marcher dans un champ de mines (qui sait quelles fonctions allouent de la mémoire?). Personnellement, j'estime qu'il est totalement irréaliste d'utiliser D sans GC, et si vous n'êtes pas fan des GC (comme moi), cela peut être assez rébarbatif.

Les définitions de tableaux naïfs en D allouent la mémoire
C'est une bête noire des animaux:

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

Apparemment, pour éviter l'attribution dans D, vous devez faire:

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

Ces petites allocations "derrière le dos" sont de bons exemples de mes deux précédents points.

Edit: Notez qu’il s’agit d’un problème connu en cours de traitement.
Edit: Ceci est maintenant corrigé. Aucune attribution n'a lieu.

Conclusion
Je me suis concentré sur les aspects négatifs de D par rapport à C ++ parce que c'est la question qui a été posée, mais s'il vous plaît, ne considérez pas ce message comme une déclaration selon laquelle C ++ est meilleur que D. Je pourrais facilement créer un plus grand nombre de lieux où D est meilleur que C ++. C'est à vous de décider lequel utiliser.

Peter Alexander
la source
J'ai regardé D il y a quelques années (avant la 2.0). Le ramassage des ordures n'était pas vraiment nécessaire à l'époque - il était là par défaut, mais vous pouvez choisir de ne pas utiliser de code de bas niveau. Ce que je pensais être le problème, c’est que je n’ai trouvé aucun moyen de revenir en arrière. Par exemple, dans un conteneur basé sur une arborescence, le code de bibliothèque peut gérer la mémoire des nœuds d’arbre eux-mêmes. L'arbre dans son ensemble peut toujours être collectionné, avec IIRC un destructeur qui collecte tous ces nœuds. Mais les objets référencés par les données dans ce conteneur doivent également être collectables - il doit y avoir un crochet pour marquer tous les éléments de données dans l'arborescence pour le GC.
Steve314
3
Vous pouvez toujours désactiver le CPG pour le code de bas niveau - Peter dit que la langue en dépend actuellement beaucoup. De plus, vous pouvez indiquer au CPG d'analyser des plages en dehors de son segment de mémoire géré à l'aide de son API: GC.addRange de core.memory .
Vladimir Panteleev
+1 pour signaler que la bibliothèque standard D est collectée et que le code GC-off n'est pas une interopérabilité transparente. Ce n’est pas quelque chose que j’y ai pensé, mais cela semble être un obstacle majeur à surmonter.
Masonk
132

Quand j'ai rejoint D development, j'étais dans la position particulière d'être l'une des personnes les plus informées sur le C ++. Maintenant, je suis dans une position encore plus singulière pour faire partie de ceux qui savent le plus à propos de D. Je ne dis pas cela pour obtenir un crédit ou des droits de vantardise, mais pour vous faire remarquer que je suis curieusement curieux. position privilégiée pour répondre à cette question. La même chose s'applique à Walter.

De manière générale, demander ce que C ++ (et j'entends par là C ++ 2011) fait mieux que D est aussi contradictoire que la question: "Si vous payez un professionnel pour nettoyer votre maison, quels sont les lieux qu’ils laisseront plus sale qu'avant? " Quelle que soit sa valeur, C ++ pouvait faire ce que D ne pouvait pas faire, cela a toujours été comme une blessure au pouce pour moi et Walter, donc presque par définition, rien que C ++ ne puisse faire qui ne soit pas à la portée de D.

Une chose qui est rarement comprise dans la conception du langage (car peu de gens ont la chance d’en faire) est qu’il ya beaucoup moins d’erreurs non forcées qu’il n’apparaît. Beaucoup d’entre nous, utilisateurs de langues, regardons une construction ou une autre et disent: "Ew! C’est tellement faux! A quoi pensaient-ils?" Le fait est que la plupart des instances maladroites dans une langue sont le résultat de quelques décisions fondamentales qui sont toutes judicieuses et souhaitables, mais qui se font fondamentalement concurrence ou se contredisent (par exemple, modularité et efficacité, intégrité et contrôle, etc.).

Cela étant dit, je vais énumérer quelques éléments auxquels je peux penser et expliquer pour chacun le lien entre le choix de D et son désir de respecter une autre charte de niveau supérieur.

  1. D suppose que tous les objets sont déplaçables par copie binaire. Cela laisse une minorité de conceptions au C ++, en particulier celles qui utilisent des pointeurs internes, c'est-à-dire une classe contenant des pointeurs à l'intérieur de lui-même. (Toute conception de ce type peut être traduite en D avec un coût d'efficacité nul ou négligeable, mais un effort de traduction serait nécessaire.) Nous avons pris la décision de simplifier considérablement le langage, de rendre la copie d'objet plus efficace avec une intervention minimale ou minimale de l'utilisateur, et d'éviter tout le maras de la construction de la copie et les références rvalue sont complètement présents.

  2. D interdit les types de genre ambigus (qui ne peuvent pas décider s'il s'agit de types valeur ou référence). De telles conceptions sont unanimement rejetées en C ++ et presque toujours fausses, mais certaines sont techniquement correctes. Nous avons fait ce choix car il interdit la plupart du temps un code incorrect et une infime fraction de code correct pouvant être redessiné. Nous croyons que c'est un bon compromis.

  3. D interdit les hiérarchies à plusieurs racines. Une précédente affiche ici était très enthousiasmée par ce sujet particulier, mais il s’agit d’un terrain bien parcouru et il n’ya aucun avantage palpable des hiérarchies sans racines sur des hiérarchies qui ont toutes une racine commune.

  4. En D, vous ne pouvez pas lancer, par exemple, un int. Vous devez lancer un objet héritant de Throwable. Nul ne conteste que la situation est meilleure en D, mais bon, c’est une chose que C ++ peut faire que D ne le peut pas.

  5. En C ++, l'unité d'encapsulation est une classe. En D c'est un module (c'est-à-dire un fichier). Walter a pris cette décision pour deux raisons: mapper naturellement l’encapsulation sur la sémantique de la protection du système de fichiers et éviter le besoin de "ami". Ce choix s’intègre très bien dans la conception globale de la modularité de D. Il serait possible de changer les choses pour ressembler davantage à C ++, mais cela forcerait les choses; Les choix d'étendue d'encapsulation de C ++ ne sont utiles que pour la conception physique de C ++.

Il pourrait y avoir une ou deux petites choses, mais globalement, ça devrait être ça.

Andrei Alexandrescu
la source
6
@DeadMG: Pour que cela fonctionne en C ++, l'objet déplacé aurait besoin d'un pointeur arrière sur l'objet pointant vers lui (pour pouvoir être mis à jour pendant la construction de la copie). Si c'est le cas, vous pouvez quand même utiliser le constructeur postblit pour mettre à jour le pointeur. S'il vous plaît, ne discutez pas contre D si vous en avez une connaissance passagère.
Peter Alexander
13
@ Peter: Cela devrait être une référence même si sa durée de vie est strictement basée sur la portée? Je devrais gaspiller le temps d’allocation dynamique et les frais généraux d’indirection, de cache et de collection parce que je veux l’aliaser? J'espère aussi que le collectionneur pourra le collecter de manière déterministe, pour une sémantique équivalente. C'est très clairement ne pas être en contrôle.
DeadMG
3
@sbi: L'existence d'une classe supérieure n'affecte en rien votre choix. Dans le réseau de type classe, il y a toujours un haut et un bas. Le fond n'est explicite que dans quelques langues . Le sommet (c.-à-d. Objet, etc.) est explicite dans plusieurs langues. Ces types existent toujours dans le concept; quand ils sont également accessibles, ils offrent simplement quelques facilités supplémentaires à l'utilisateur de la langue sans causer de problèmes.
Andrei Alexandrescu
6
@quant_dev: vous serez heureux d'apprendre qu'un projet GSoC déjà en bonne voie est axé sur l'algèbre linéaire haute performance utilisant BLAS. Il fournit également des implémentations "naïves" des primitives appropriées à des fins de test et d'analyse comparative. Pour répondre à votre deuxième question, Java place la barre assez bas pour comparer les bibliothèques numériques. Il aura toujours la peine de dépasser une barrière JNI pour accéder à des librairies d'algèbre hautes performances, et la syntaxe sera mauvaise car Java ne surcharge pas l'opérateur.
Andrei Alexandrescu
4
@PeterAlexander: DeadMG est sur le point. "Vous ne devriez pas utiliser les pointeurs sur les types de valeur" n'ignore pas du fait que les pointeurs de toutes les langues sont généralement utilisés avec des types de valeur (vous attendez-vous vraiment à voir un Object*aussi largement utilisé qu'un int*??) Et D semble ignorer complètement la pénalité de performance, ou le prétendant n'existe pas. C'est évidemment faux - le manque de mémoire cache est assez visible dans de nombreux cas, donc C ++ aura toujours cet avantage en
termes de
65

Je pense que vous allez avoir beaucoup de mal à trouver beaucoup dans D qui est objectivementpire que C ++. La plupart des problèmes avec D où vous pourriez objectivement dire que c'est pire sont soit des problèmes de qualité d'implémentation (qui sont généralement dus à la jeunesse du langage et de l'implémentation qui ont été corrigés à une vitesse vertigineuse), ou ce sont des problèmes avec un manque de bibliothèques tierces (qui viendra avec le temps). Le langage lui-même est généralement meilleur que le C ++, et les cas où C ++, en tant que langage, est meilleur sont généralement ceux où il y a un compromis où C ++ est allé dans un sens et D est allé dans un autre, ou lorsque quelqu'un a des raisons subjectives pour lesquelles il a pense que l'un est meilleur qu'un autre. Mais le nombre de raisons objectives pour lesquelles C ++, en tant que langage, est meilleur est susceptible d’être rare.

En fait, je dois vraiment me casser la tête pour trouver les raisons pour lesquelles le langage C ++ est meilleur que le langage D. Ce qui me vient généralement à l’esprit est une question de compromis.

  1. Comme le const de D est transitif et que le langage est immuable , il a des garanties bien plus solides que le C ++ const, ce qui signifie que D n’a pas et ne peut pas avoir mutable. Il ne peut pas avoir de logique . Vous obtenez donc un gain énorme avec le système const de D, mais dans certaines situations, vous ne pouvez pas utiliser constcomme vous le feriez en C ++.

  2. D n'a qu'un seul opérateur de conversion, alors que C ++ en a 4 (5 si vous comptez l'opérateur de conversion C). Cela facilite le traitement des castes en D dans le cas général, mais est problématique lorsque vous voulez réellement les complications / avantages supplémentaires que const_castfournissent ses frères. Mais D est en fait assez puissant pour que vous puissiez utiliser des modèles pour implémenter les transts C ++. Si vous les voulez vraiment, vous pouvez les avoir (et ils peuvent même se retrouver dans la bibliothèque standard de D à un moment donné).

  3. D a beaucoup moins de conversions implicites que C ++ et est beaucoup plus susceptible de déclarer que deux fonctions sont en conflit (ce qui vous oblige à préciser davantage la fonction que vous voulez dire - avec des conversions ou en donnant le chemin complet du module ) Parfois, cela peut être ennuyeux, mais cela évite toutes sortes de problèmes de détournement de fonction . Vous savez que vous appelez vraiment la fonction que vous voulez dire.

  4. Le système de modules de D est bien plus propre que celui de C ++ (sans oublier, beaucoup plus rapide à la compilation), mais il lui manque tout type d’espace de nommage au-delà des modules eux-mêmes. Donc, si vous voulez un espace de noms dans un module, vous devez utiliser la route Java et utiliser des fonctions statiques sur une classe ou une structure. Cela fonctionne, mais si vous voulez vraiment un espacement de noms, ce n'est évidemment pas aussi net qu'un véritable espacement de noms. Cependant, dans la plupart des situations, les modules vous fournissent une grande quantité de noms (et assez sophistiqués pour les conflits, par exemple).

  5. Comme Java et C #, D possède un héritage unique plutôt qu’un héritage multiple, mais contrairement à Java et C #, il vous offre des moyens fantastiques d’obtenir le même effet sans tous les problèmes que l’héritage multiple de C ++ a (l’héritage multiple de C ++ peut devenir très compliqué.) a l'heure). Non seulement D ont des interfaces , mais il a mixins chaîne , mixins modèle , et alias ce . Ainsi, le résultat final est sans doute plus puissant et ne présente pas tous les problèmes inhérents à l'héritage multiple de C ++.

  6. Semblable à C #, D sépare les structures et les classes . Les classes sont des types de référence qui ont un héritage et qui en sont dérivés Object, alors que les structures sont des types de valeur sans héritage. Cette séparation peut être à la fois bonne et mauvaise. Il supprime le problème de découpage classique en C ++ et permet de séparer les types qui sont réellement des types de valeur de ceux qui sont supposés être polymorphes, mais au début, au moins, la distinction pourrait être gênante pour un programmeur C ++. En fin de compte, il y a un certain nombre d'avantages, mais cela vous oblige à traiter vos types un peu différemment.

  7. Les fonctions membres des classes sont polymorphes par défaut. Vous ne pouvez pas les déclarer non virtuels . Il appartient au compilateur de décider s’ils peuvent le faire (ce qui n’est vraiment le cas que s’ils sont finaux et qu’ils ne remplacent pas une fonction d’une classe de base). Donc, cela pourrait être un problème de performance dans certains cas. Cependant, si vous n'avez vraiment pas besoin du polymorphisme, tout ce que vous avez à faire est d'utiliser des structures , et ce n'est pas un problème.

  8. D possède un ramasse - miettes intégré . Beaucoup de C ++ considèrent que c'est un sérieux inconvénient, et à la vérité, sa mise en œuvre pourrait nécessiter un travail sérieux. Les choses s'améliorent, mais ce n'est certainement pas encore à égalité avec le ramasse-miettes de Java. Cependant, ceci est atténué par deux facteurs. Premièrement, si vous utilisez principalement des structures et d'autres types de données sur la pile, le problème n'est pas grave. Si votre programme n'alloue pas et ne désalloue pas constamment des choses sur le tas, tout ira bien. Et deuxièmement, vous pouvez ignorer le ramasse - miettes si vous le souhaitez et utiliser simplement les lettres C mallocet free. Certaines fonctionnalités linguistiques (telles que le découpage en matrice) que vous devrez éviter ou avec prudence, et une partie de la bibliothèque standard ne sera pas réellement utilisable sans au moins quelques utilisations du GC (en particulier du traitement de chaîne), mais vous pouvez écrire en D sans utiliser le garbage collector si tu veux vraiment. La chose intelligente à faire est probablement de l’utiliser généralement et de l’éviter ensuite lorsque le profilage montre que cela pose des problèmes pour le code critique en termes de performances, mais vous pouvez l’éviter complètement si vous le souhaitez. Et la qualité de la mise en œuvre du GC s'améliorera avec le temps, éliminant ainsi bon nombre des problèmes que l'utilisation d'un GC peut causer. Donc, au final, le GC ne sera pas un aussi gros problème et, contrairement à Java, vous pouvez l'éviter si vous le souhaitez.

Il y en a probablement aussi d'autres, mais c'est ce que je peux proposer pour le moment. Et si vous remarquerez, ce sont tous des compromis. D a choisi de faire certaines choses différemment du C ++, qui présentent des avantages certains par rapport à la façon dont C ++ les fait, mais qui présentent également certains inconvénients. Ce qui est mieux dépend de ce que vous faites, et dans de nombreux cas, cela ne semblera probablement qu'empirer au début et vous ne rencontrerez plus de problème une fois que vous y serez habitué. En fait, les problèmes de D vont généralement être nouveaux et causés par de nouvelles choses que d’autres langages n’ont pas encore utilisées ou qui ont été faites de la même manière que D. Globalement, D a très bien appris des erreurs de C ++.

Et D, en tant que langage, s’améliore sur le C ++ de tant de façons que je pense que c’est généralement le cas que D est objectivement meilleur.

  1. D a une compilation conditionnelle . C'est l'une des fonctionnalités que je manque souvent lorsque je programme en C ++. Si C ++ l’ajoute, alors C ++ s’améliorera à pas de géant en ce qui concerne des éléments tels que les modèles.

  2. D a une réflexion au moment de la compilation .

  3. Les variables sont locales aux threads par défaut, mais peuvent l'être sharedsi vous le souhaitez. Cela rend la gestion des threads bien plus propre qu'en C ++. Vous êtes en contrôle total. Vous pouvez utiliser immutableet la transmission de messages pour communiquer entre les threads ou créer des variables sharedet le faire à la manière C ++ avec des mutex et des variables de condition. Même cela est amélioré par rapport à C ++ avec l'introduction de synchronized (similaire à C # et Java). La situation de threading de D est donc bien meilleure que celle de C ++.

  4. Les modèles de D sont beaucoup plus puissants que les modèles de C ++, vous permettant de faire beaucoup plus, beaucoup plus facilement. Et avec l’ajout de contraintes de modèles, les messages d’erreur sont bien meilleurs qu’en C ++. D rend les templates très puissants et utilisables. Ce n'est pas un hasard si l'auteur de Modern C ++ Design est l'un des principaux collaborateurs de D. Je trouve que les modèles C ++ manquent sérieusement par rapport aux modèles D, ce qui peut être très frustrant lors de la programmation en C ++.

  5. D possède une programmation contractuelle intégrée .

  6. D possède un cadre de test unitaire intégré .

  7. D prend en charge les codes unicode avec string(UTF-8), wstring(UTF-16) et dstring(UTF-32). Cela facilite la gestion de l’unicode. Et si vous souhaitez simplement utiliser stringet ne vous souciez généralement pas de l'unicode, vous pouvez - bien que certaines notions de base relatives à Unicode soient utiles pour certaines fonctions de bibliothèque standard.

  8. La surcharge d'opérateurs de D est beaucoup plus agréable que celle de C ++, vous permettant d'utiliser une fonction pour surcharger plusieurs opérateurs en même temps. Un bon exemple en est le cas où vous devez surcharger les opérateurs arithmétiques de base et où leurs implémentations sont identiques, à l’exception de l’opérateur. Les mixins de chaînes en font un jeu d'enfant, vous permettant d'avoir une définition de fonction simple pour tous.

  9. Les tableaux de D sont bien meilleurs que ceux de C ++. Non seulement sont-ils un type approprié avec une longueur, mais ils peuvent être ajoutés et redimensionnés. Les concaténer est facile. Et le meilleur de tous, ils ont trancher . Et c'est un avantage énorme pour un traitement efficace des tableaux. Les chaînes de caractères sont des tableaux de caractères en D, et ce n'est pas un problème (en fait, c'est génial!), Car les tableaux de D sont si puissants.

Je pourrais continuer encore et encore. Un grand nombre des améliorations apportées par D sont de petites choses (comme utiliser thisdes noms de constructeurs ou interdire si des instructions ou des corps de boucles où un point-virgule est tout leur corps), mais certaines d'entre elles sont assez volumineuses, et lorsque vous les additionnez, permet une expérience de programmation bien meilleure. C ++ 0x ajoute certaines des fonctionnalités de D dont C ++ était manquant (par exemple, autoet lambdas), mais malgré toutes ses améliorations, il n'y aura toujours pas grand-chose qui soit objectivement meilleur en langage C ++ que D.

Il ne fait aucun doute qu’il existe de nombreuses raisons subjectives de préférer l’une sur l’autre, et l’immaturité relative de la mise en œuvre de D peut parfois poser problème (même si elle s’est améliorée très rapidement récemment, surtout depuis que les dépôts ont été déplacés vers github ). et le manque de bibliothèques tierces peut poser problème (bien que le fait que D puisse facilement appeler des fonctions C - et dans une moindre mesure, des fonctions C ++ - atténue définitivement le problème). Mais ce sont des problèmes de qualité de mise en œuvre plutôt que des problèmes avec la langue elle-même. Et à mesure que la qualité des problèmes de mise en œuvre sera corrigée, il deviendra d'autant plus agréable d'utiliser D.

Donc, je suppose que la réponse courte à cette question est "très peu". D, en tant que langage, est généralement supérieur au C ++.

Jonathan M Davis
la source
2
Les langages de récupération de place utilisent 2 à 5 fois plus de mémoire que les langs non-GC (selon le discours d'Alexandrescu sur YT), ce qui pose définitivement un problème si (l'utilisation de la mémoire) constitue le goulot d'étranglement.
NoSenseEtAl
9

Utilisation de la mémoire RAII et de la pile

D 2.0 n'autorise pas RAII sur la pile car il a supprimé la valeur du scopemot clé lors de l'allocation d'instances de classe sur la pile.

Vous ne pouvez pas effectuer d'héritage de type valeur en D, ce qui vous oblige effectivement à effectuer une allocation de tas pour toute forme de RAII.
C'est-à-dire, sauf si vous utilisez emplace, mais c'est très pénible à utiliser, car vous devez allouer la mémoire à la main. (Je n'ai pas encore trouvé pratique à utiliser emplacedans D.)

Mehrdad
la source
6

C ++ est bien meilleur pour vous forcer à être prolixe. Cela peut être meilleur ou pire à vos yeux, selon que vous aimiez l'inférence ou la verbosité.

Comparez la mémorisation au moment de l’exécution en 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...);
    });
}

avec la même chose en 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));
    };
}

Remarquez, par exemple, le surcroît de verbosité avec template <typename ReturnType, typename... Args>versus (F), Args...versus Args, args...versus args, etc.
Pour le meilleur ou pour le pire, C ++ est plus détaillé.

Bien sûr, vous pouvez aussi le faire en 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));
        };
    }
}

et ils sembleraient presque identiques, mais alors cela exigerait un delegate, alors que l'original acceptait n'importe quel objet appelable. (La version C ++ 0x nécessite unstd::function objet, alors quoi qu'il en soit, ses entrées sont plus détaillées et plus restrictives ... ce qui peut être utile si vous aimez la verbosité, voire mauvais si vous ne l'aimez pas.)

Mehrdad
la source
2

Je ne connais pas grand chose à propos de D, mais beaucoup, beaucoup de programmeurs C ++ que je connais ne l'aiment pas du tout, et je dois personnellement en convenir - je n'aime pas le look de D et je ne le prendrai pas de plus près.

Afin de comprendre pourquoi D ne gagne pas en puissance, vous devez commencer par comprendre ce qui attire les gens vers C ++. En un mot, la raison numéro un est le contrôle. Lorsque vous programmez en C ++, vous avez un contrôle total sur votre programme. Voulez-vous remplacer la bibliothèque standard? Vous pouvez. Voulez-vous faire des lancers de pointeur dangereux? Vous pouvez. Voulez-vous violer const-correct? Vous pouvez. Voulez-vous remplacer l'allocateur de mémoire? Vous pouvez. Vous voulez copier la mémoire brute sans vous soucier de son type? Si tu le veux vraiment. Voulez-vous hériter de plusieurs implémentations? C'est ton enterrement. Enfer, vous pouvez même obtenir des bibliothèques de récupération de place, comme le collecteur Boehm. Ensuite, vous rencontrez des problèmes tels que les performances, qui suivent de près le contrôle: plus un programmeur a de contrôle, plus il peut optimiser son programme.

Voici quelques éléments que j'ai vus lors d'une petite recherche et de discussions avec quelques personnes qui l'ont essayée:

Hiérarchie de types unifiée. Les utilisateurs C ++ utilisent très rarement l'héritage, la plupart des programmeurs C ++ préfèrent la composition et les types ne doivent être liés par l'héritage que s'il existe une très bonne raison de le faire. Le concept d'objet viole fortement ce principe en liant tous les types. En outre, cela enfreint l'un des principes les plus fondamentaux du C ++: vous n'utilisez que ce que vous voulez. Ne pas avoir le choix d'hériter d'Object, et les coûts qui en découlent, vont tout à fait à l'encontre de ce que C ++ représente comme langage pour donner au programmeur le contrôle de son programme.

J'ai entendu parler de problèmes de fonctions et de délégués. Apparemment, D possède à la fois des fonctions et des délégués en tant que types de fonction appelables à l'exécution, et ils ne sont pas identiques, mais ils sont interchangeables ou ... quelque chose? Mon ami a eu pas mal de problèmes avec eux. C’est définitivement une dégradation de C ++, ce qui est déjà fait std::functionet vous avez terminé.

Ensuite, vous avez la compatibilité. D n'est pas particulièrement compatible avec C ++. Je veux dire, aucun langage n'est compatible avec le C ++, avouons-le, à l'exception de C ++ / CLI, qui est une sorte de triche, mais en tant que barrière à l'entrée, il faut le mentionner.

Ensuite, il y a d'autres choses. Par exemple, il suffit de lire l’entrée Wikipedia.

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

printfest l’une des fonctions les moins sûres jamais conçues, dans la même famille que de gros problèmes comme celui getsde l’ancienne bibliothèque C Standard. Si vous le recherchez sur Stack Overflow, vous trouverez de nombreuses questions relatives à son utilisation abusive. Fondamentalement, printfest une violation de DRY- vous donnez le type dans la chaîne de format, puis vous le redonnez quand vous lui donnez un argument. Une violation de DRY dans laquelle, si vous vous trompez, de très mauvaises choses se produisent, par exemple si vous modifiez un typedef d'un entier 16 bits en un 32 bits. Il n’est pas non plus extensible du tout. Imaginez ce qui se passerait si tout le monde inventait ses propres spécificateurs de format. Les iostreams de C ++ peuvent être lents et leur choix d'opérateur peut ne pas être le plus grand. Leur interface peut utiliser le travail, mais leur sécurité est fondamentalement garantie, et DRY n'est pas violé et ils peuvent être étendus facilement. Ce n'est pas quelque chose dont on peut parler printf.

Pas d'héritage multiple. Ce n'est vraiment pas la méthode C ++. Les programmeurs C ++ s'attendent à avoir un contrôle total sur leur programme et le langage qui applique ce dont vous ne pouvez pas hériter enfreint ce principe. En outre, cela rend l'héritage (encore plus) fragile, car si vous passez d'un type d'interface à une classe parce que vous voulez fournir une implémentation par défaut ou quelque chose, tout le code de votre utilisateur est soudainement cassé. Ce n'est pas une bonne chose.

Un autre exemple est stringet wstring. En C ++, il est déjà très pénible de devoir convertir entre eux. Cette bibliothèque prend en charge l’Unicode. Cette ancienne bibliothèque C n’utilise que const char*la même fonction et doit écrire différentes versions de la même fonction en fonction du type d’argument de chaîne souhaité. Notamment, les en-têtes Windows, par exemple, contiennent des macros extrêmement irritantes pour faire face au problème, qui peuvent souvent interférer avec votre propre code. Ajouter dstringà la combinaison ne fera qu'empirer les choses, car maintenant, au lieu de deux types de chaînes, vous devez en gérer trois. Avoir plus d'un type de chaîne va augmenter les problèmes de maintenance et introduire du code répétitif traitant des chaînes.

Scott Meyers écrit:

D est un langage de programmation conçu pour aider les programmeurs à relever les défis du développement logiciel moderne. Pour ce faire, il favorise des modules interconnectés via des interfaces précises, une fédération de paradigmes de programmation étroitement intégrés, une isolation de threads imposée par le langage, une sécurité de type modulaire, un modèle de mémoire efficace, etc.

L'isolation de thread appliquée par la langue n'est pas un avantage. Les programmeurs C ++ attendent un contrôle total de leurs programmes, et le langage qui force quelque chose n’est certainement pas ce que le médecin a prescrit.

Je vais aussi mentionner la manipulation de chaîne au moment de la compilation. D a la capacité d'interpréter le code D au moment de la compilation. Ce n'est pas un plus. Prenez en compte les énormes maux de tête causés par le préprocesseur relativement limité de C, bien connu de tous les programmeurs vétérans du C ++, puis imaginez à quel point cette fonctionnalité sera utilisée à mauvais escient. La possibilité de créer du code D au moment de la compilation est excellente, mais elle devrait être sémantique et non syntaxique.

De plus, vous pouvez vous attendre à un certain réflexe. D a garbage collection, que les programmeurs C ++ associeront à des langages tels que Java et C # qui lui sont assez directement opposés dans les philosophies, et les similitudes syntaxiques les rappelleront également. Ce n'est pas nécessairement objectivement justifiable, mais c'est quelque chose qu'il convient de noter.

Fondamentalement, il n'offre pas beaucoup de choses que les programmeurs C ++ ne peuvent pas déjà faire. Il est peut-être plus facile d'écrire un métaprogramme factoriel en D, mais nous pouvons déjà écrire des métaprogrammes factoriels en C ++. Peut-être qu'en D, vous pouvez écrire un traceur de rayons au moment de la compilation, mais personne ne veut vraiment le faire de toute façon. Comparé aux violations fondamentales de la philosophie C ++, ce que vous pouvez faire en D n’est pas particulièrement remarquable.

Même si ces problèmes ne sont que des problèmes à la surface, je suis presque sûr que le fait que D ne ressemble pas du tout à C ++ est probablement une bonne raison pour que de nombreux programmeurs C ++ ne migrent pas vers D. Peut-être que D doit faire un meilleur travail de publicité lui-même.

DeadMG
la source
9
@DeadMG: C'est 100% incorrect et il manque le point de dire que c'est définitivement une rétrogradation du C ++, ce qui vient d'être fait std::functionet vous avez terminé. Pourquoi? Parce que vous avez également, par exemple, des pointeurs de fonction. C'est exactement la même chose dans D: les "fonctions" de D sont des pointeurs de fonction et les "délégués" de D sont identiques à ceux de C ++ std::function(sauf qu'ils sont intégrés). Il n'y a pas de «déclassement» où que ce soit - et il existe une correspondance 1: 1 entre eux, donc cela ne devrait pas du tout être déroutant si vous êtes familier avec C ++.
Mehrdad
10
@ Mark Trapp: Je dois avouer que je ne comprends pas très bien votre position sur le sujet. Les commentaires ne doivent pas être utilisés, vous savez, commenter une réponse?
Klickverbot
6
@ Mark Trapp: Mon point est que la plupart des commentaires ici n'étaient pas obsolètes (la méta discussion que vous avez liée s'applique spécifiquement aux suggestions qui ont déjà été intégrées dans le message d'origine), mais a souligné des inexactitudes factuelles dans le message, qui sont toujours présentes .
Klickverbot
7
Remarque sur le format: la fonction de format de D est dactylographiée (résout les problèmes de sécurité / débordement) et ne viole pas DRY car la chaîne de format spécifie uniquement le format des arguments, pas leur type. Ceci est possible grâce aux variadiques typesafe de D. Par conséquent, cette objection au moins est totalement invalide.
Justin W
17
@Mark: Quelle que soit la politique actuelle à leur égard, je trouve cela stupide et empêche que les discussions de commentaires soient supprimées . Je pense que cette réponse a fait l'objet de discussions approfondies (qui m'intéressent maintenant), mais je n'en suis pas sûre et je n'ai aucun moyen de le savoir. Cette pièce à laquelle vous avez accédé a plus de 10 000 messages et je n'ai aucune chance de trouver une discussion dont je me souviens avoir eu lieu, mais dont je ne me souviens pas du contenu. Les discussions concernant cette réponse appartiennent ici, à cette réponse , et non à un forum de discussion, où elles pourraient être mêlées à des discussions sur le sexe, la drogue et le rock'n'roll.
sbi
1

Une des choses que j'apprécie en C ++ est la possibilité de documenter un argument de fonction ou une valeur renvoyée sous la forme d'une référence C ++ au lieu d'un pointeur, ce qui implique de prendre une non- nullvaleur.

Version 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);
}

Version 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;
}

Pour être juste, vous pouvez vous rapprocher du C ++ en faisant Aun D structet en marquant l' foo()argument comme un ref(les classes sont des types de référence et les struct sont des types de valeur en D, similaires au C #).

Je pense qu’il est prévu de créer un NonNullablemodèle pour les classes sous la forme d’une bibliothèque D standard. Même si j'aime la brièveté de just Type&compare à NonNullable(Type), et préférerais non-nullable par défaut (rendre quelque chose comme Typeet Nullable(Type)). Mais il est trop tard pour changer cela pour D et je m'éloigne maintenant du sujet.

la lumière
la source
3
Les arguments de fonction et les valeurs de retour dans D peuvent être marqués avec refpour vous donner le même effet que celui de C ++ &. La seule différence majeure est que refcela ne prendra pas de temps, même si c'est const.
Jonathan M Davis
J'aime la façon dont le renvoi de références à des variables locales est interdit en D, je n'étais pas au courant de cela avant d'avoir lu votre commentaire. Cependant, vous pouvez toujours renvoyer une référence null non locale sans y penser de manière à ce que l'opérateur de déréférence C vous fasse réfléchir.
Lumor
Vous confondez les choses. Les classes sont toujours des références, ce qui est distinct de la référence. Les références en D sont similaires aux références en Java. Ce sont des pointeurs gérés. Passer ou revenir par arbitre, c'est comme passer ou revenir avec & en C ++. Passer une référence de classe par ref revient à passer un pointeur en C ++ avec & (par exemple, A * &). Les classes ne vont pas sur la pile. Oui, NonNullable permettrait d’avoir une référence de classe qui serait garantie non non nulle, mais cela est complètement séparé de ref. Ce que vous essayez de faire dans le code C ++ ne fonctionne pas en D car les classes ne vont pas dans la pile. Les structures font.
Jonathan M Davis
1
Alors oui, il serait bien de pouvoir avoir une référence de classe non nullabe, mais C ++ réussit à faire ce que vous montrez car cela permet aux classes d'être sur la pile et de déréférencer les pointeurs. Et même si vous pouvez déréférencer les pointeurs dans D, les classes sont des références et non des pointeurs. Vous ne pouvez donc pas les déréférencer. Puisque vous ne pouvez pas les mettre sur la pile, et vous ne pouvez pas les déréférencer, il n’ya aucun moyen intégré à D d’avoir une classe qui ne puisse pas être nulle. Il est une perte, mais nonnullable corrigera, et les gains de la séparation des structs et les classes sont généralement plus de toute façon.
Jonathan M Davis
2
Les références C ++ ne peuvent pas être nulles selon le standard de langage (dire "probablement ne sera pas nul" est incorrect puisqu'il ne peut pas être nul). Je souhaite qu'il y ait un moyen de refuser null comme valeur valide pour une classe.
Jsternberg
1

La chose la plus importante pour que C ++ "fasse mieux" que D est l’ interfaçage avec les bibliothèques existantes . Divers moteurs 3D, OpenCL et similaires. Comme D est nouveau, il y a beaucoup moins de bibliothèques à choisir.

Une autre différence importante entre le C ++ et le D réside dans le fait que le C ++ compte plusieurs fournisseurs indépendants sur le plan financier, mais à partir de 2014, le D n'en a pratiquement plus qu'un , l'équipe de deux personnes qui l'a créé. Il est intéressant de noter que le "principe de deuxième source" selon lequel les projets ne doivent jamais dépendre de la technologie, les composants, qui ne possèdent qu'un seul fournisseur, semblent également valables, même pour les logiciels.

À titre de comparaison, la première version de l'interprète Ruby a été écrite par l'inventeur de Ruby, le Yukihiro Matsumoto, mais l'interprète Ruby traditionnel de l'ère de 2014 a été écrit pratiquement à partir de rien par d'autres personnes. Par conséquent, Ruby peut être considéré comme un langage qui compte plusieurs fournisseurs indépendants du point de vue financier. D, d’autre part, peut être une technologie impressionnante, mais cela dépend des quelques développeurs qui la développent.

L’histoire de Java montre que, même si une technologie, dans ce cas-ci, a un fin financier simple mais unique, il existe un grand risque que la technologie soit essentiellement vidé, indépendamment de l’énorme base d’utilisateurs de l’entreprise. Une citation de la fondation Apache Software , où le CE signifie "Comité exécutif":

Oracle a fourni à la CE une demande de spécification et une licence Java SE 7 contradictoires, limitant considérablement la distribution d'implémentations indépendantes de la spécification et, surtout, interdisant la distribution d'implémentations open source indépendantes de la spécification.

En guise de note historique, on peut dire que les applets Java avaient une toile 3D accélérée matériellement avant le développement du WebGL HTML5. Le problème de la vitesse de démarrage des applets Java aurait pu être résolu si le projet Java avait été un projet de communauté, mais les dirigeants du seul financier du système Java, Sun Microsystems, n’avaient pas jugé suffisamment important de corriger l’implémentation Java. Le résultat final: une toile HTML5 par plusieurs fournisseurs en tant que "réplique du pauvre" des frameworks d'interface graphique Java (Swing, etc.). Il est intéressant de noter que côté langage, le langage de programmation Python présente les mêmes avantages que ceux promis par Java: écriture unique, exécution sur tous les serveurs, quel que soit le matériel, à condition que l’application Python ne soit pas compilée en code machine. Le Python est aussi vieux / jeune que le Java, mais contrairement au Java, il

Sommaire:

Lors de l'évaluation d'une technologie pour une utilisation en production, les propriétés les plus importantes des technologies sont les personnes qui la développent, le processus social, le lieu du développement et le nombre d'équipes de développement financièrement indépendantes.

Qu'il soit plus avantageux d'utiliser la technologie T1 par rapport à la technologie T2 dépend des vendeurs de technologies et si la technologie T1 permet de résoudre des problèmes liés au projet à moindre coût que la T2. Par exemple, si le problème du fournisseur unique était ignoré, alors, pour les systèmes d’information, Java serait une technologie «meilleure» que le C ++, car les fichiers binaires Java n’ont pas besoin de recompilation lors du déploiement sur du nouveau matériel et de la quantité de travail de développement logiciel lié à la gestion de la mémoire. est plus petit pour le Java que pour le C ++. Les projets développés de A à Z, par exemple les projets qui ne dépendent pas d'autres bibliothèques, pourraient être moins coûteux à développer en D qu'en C ++, mais d'un autre côté, le C ++ a plus d'un fournisseur et est donc moins risqué à long terme . (L’exemple Java, où Sun Microsystems était presque OK,

Une solution de contournement possible à certaines des limitations C ++

L’une des solutions possibles à certaines des limitations du C ++ consiste à utiliser un modèle de conception, dans lequel la tâche initiale est décomposée et les pièces sont "triées" (classées, regroupées, divisées, divisées en d’autres mots). même chose) à 2 classes: tâches liées à la logique de contrôle , où les modèles d’accès à la mémoire ne permettent pas la localité; traitement du signal comme tâches , où la localité est facilement réalisée. Les tâches "similaires" du traitement du signal peuvent être mises en oeuvre sous forme d'un ensemble de programmes de console relativement simplistes. Les tâches liées à la logique de contrôle sont toutes placées dans un seul script écrit en Ruby ou en Python ou dans un autre format, dans lequel le confort de développement logiciel a une priorité supérieure à la vitesse.

Pour éviter une initialisation coûteuse et une copie des données entre processus du système d'exploitation, l'ensemble de petites applications de console C ++ peut être implémenté en tant que programme C ++ unique démarré en tant que "servlet" par Ruby / Python / etc. scénario. Le Ruby / Python / etc. Le script ferme la servlet avant de quitter. Communication entre le "servlet" et le Ruby / Python / etc. Le script se déroule sur un canal nommé Linux ou un mécanisme similaire.

Si la tâche initiale ne se prête pas facilement à la division en pièces pouvant être classées dans les 2 classes susmentionnées, il peut être intéressant d'essayer de reformuler le problème afin que la tâche initiale change.

Martin Vahi
la source