web-dev-qa-db-fra.com

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

J'ai récemment appris D et je commence à me familiariser avec la langue. Je sais ce qu'il offre, je ne sais pas encore comment tout utiliser, et je ne sais pas grand chose sur les idiomes D et ainsi de suite, mais j'apprends.

J'aime D. C'est un langage agréable, étant, à certains égards, une énorme mise à jour de C, et bien faite. Aucune des fonctionnalités ne semble "boulonnée", mais en fait assez bien pensée et bien conçue.

Vous entendrez souvent que D est ce que C++ aurait dû être (je laisse la question de savoir si cela est vrai pour tout le monde de décider eux-mêmes afin pour éviter des guerres de flammes inutiles). J'ai également entendu de la part de plusieurs programmeurs C++ qu'ils apprécient D beaucoup plus que C++.

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

D a été conçu par des programmeurs C++ très qualifiés ( Walter Bright et Andrei Alexandresc , avec l'aide de la communauté D) pour résoudre de nombreux problèmes rencontrés par C++, mais était Y a-t-il quelque chose qui ne s'est pas amélioré après tout? Quelque chose qu'il a manqué? Quelque chose que vous pensez n'était pas une meilleure solution?

Notez également que je parle de D 2. , pas D 1. .

135
Anto

La plupart des choses que C++ "fait" mieux que D sont des 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 de plus en plus de toutes les choses externes que vous attendrait d'une langue plus mature. C'est incontestable.

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 voici quelques-uns que je peux énumérer du haut de ma tête:

C++ a un système de type mieux pensé
Il y a actuellement pas mal de problèmes avec le système de typage 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 la structure contient des références ou des pointeurs d'objet de classe en raison de la transitivité de const et de la façon dont les constructeurs postblit travaillent sur les types de 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 (l'introduction de constructeurs de copie de style C++ serait une solution), mais c'est actuellement un problème majeur dans le langage.

Un autre problème qui m'a mis sur écoute est le manque de const logique (c'est-à-dire pas de mutable comme en C++). C'est idéal pour écrire du code thread-safe, mais il est difficile (impossible?) De faire une initialisation paresseuse dans les objets const (pensez à une fonction const 'get' qui construit et met en cache la valeur retournée au premier appel).

Enfin, étant donné ces problèmes existants, je m'inquiète de la façon dont le reste du système de type (pure, shared, etc.) interagira avec tout le reste de la langue une fois mis à utilisation. La bibliothèque standard (Phobos) utilise actuellement très peu le système de type avancé de D, donc je pense qu'il est raisonnable de se demander si elle résiste au stress. Je suis sceptique, mais optimiste.

Notez que C++ a certains types de verrues système (par exemple, const non transitive, nécessitant iterator ainsi que const_iterator) ce qui le rend assez moche, mais bien que le système de type de C++ soit un peu erroné par parties, il ne vous empêche pas de faire le travail comme le fait parfois D.

Edit: Pour clarifier, je crois que C++ a un meilleur système de type bien pensé - pas nécessairement meilleur - si cela a du sens. Essentiellement, en D, je pense qu'il y a un risque à utiliser tous les aspects de son système de type qui n'est pas présent en C++.

D est parfois un peu trop pratique
L'une des critiques que vous entendez souvent à propos du C++ est qu'il vous cache certains problèmes de bas niveau, par exemple missions simples comme a = b; pourrait faire beaucoup de choses comme appeler des opérateurs de conversion, appeler des opérateurs d'attribution de surcharge, etc., ce qui peut être difficile à voir dans le code. Certaines personnes aiment ça, d'autres non. De toute façon, en D, c'est pire (mieux?) À cause de choses comme opDispatch, @property, opApply, lazy qui ont le potentiel de changer du code d'apparence innocente en des choses auxquelles vous ne vous attendez pas.

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

D nécessite un ramasse-miettes
Cela pourrait être considéré comme controversé car il est possible d'exécuter 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 serait comme marcher dans un champ de mines (qui sait quelles fonctions allouent la mémoire?). Personnellement, je pense qu'il est totalement impossible d'utiliser D sans GC, et si vous n'êtes pas un fan des GC (comme moi), cela peut être assez rebutant.

définitions de tableaux naïfs dans la mémoire d'allocation D
Voici ma bête noire:

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'allocation en 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 votre dos" sont de bons exemples de mes deux points précédents.

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

Conclusion
. où D est meilleur que C++. C'est à vous de décider lequel utiliser.

124
Peter Alexander

Quand j'ai rejoint le développement D, j'étais dans la position particulière d'être l'une des personnes qui connaissent le mieux le C++. Maintenant, je suis dans une position encore plus particulière d'être aussi l'une des personnes qui en savent le plus sur D. Je ne dis pas cela au crédit approprié ou aux droits de vantardise autant que de remarquer que je suis curieusement position privilégiée pour répondre à cette question. Il en va de même pour Walter.

En gros, 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 endroits où ils partiront plus sale qu'avant? " Quelle que soit la valeur, le C++ pouvait faire ce que D ne pouvait pas, il a toujours été comme un pouce douloureux pour moi et Walter, donc presque par définition, il n'y a rien que le C++ puisse faire qui ne soit pas à la portée de D.

Une chose qui est rarement comprise dans la conception d'un langage (car peu de gens ont la chance d'en faire) est qu'il y a beaucoup moins d'erreurs non forcées qu'il n'y paraît. Beaucoup d'entre nous, utilisateurs de langues, regardons une construction ou une autre et disent: "Ew! C'est tellement faux! Que pensaient-ils?" Le fait est que la plupart des cas gênants dans une langue sont le résultat de quelques décisions fondamentales qui sont toutes saines et souhaitables mais qui sont fondamentalement en concurrence ou se contredisent (par exemple, modularité et efficacité, concision et contrôle, etc.).

Avec tout cela à l'esprit, je vais énumérer quelques choses auxquelles je peux penser, et pour chacune, j'expliquerai comment le choix de D découle d'un désir de remplir une autre charte de niveau supérieur.

  1. D suppose que tous les objets sont mobiles par copie au niveau du bit. 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. (Une telle conception peut être traduite en D ou avec un coût d'efficacité négligeable en D, mais cela impliquerait un effort de traduction.) Nous avons pris cette décision pour simplifier considérablement la langue, rendre la copie d'objets plus efficace sans intervention de l'utilisateur ou avec un minimum et éviter la totalité du marécage de construction de copie et les références rvalue figurent toutes ensemble.

  2. D interdit les types de genre ambigu (qui ne peuvent pas décider s'il s'agit de types valeur ou référence). Ces conceptions sont unanimement rejetées en C++ et presque toujours fausses, mais certaines d'entre elles sont techniquement correctes. Nous avons fait ce choix car il interdit la plupart du temps le code incorrect et seulement une petite fraction du code correct qui peut être repensé. Nous pensons que c'est un bon compromis.

  3. D interdit les hiérarchies multi-racines. Une affiche précédente ici a été très enthousiasmée par ce sujet particulier, mais c'est un terrain bien foulé et il n'y a aucun avantage palpable des hiérarchies sans racines par rapport aux hiérarchies qui ont toutes une racine commune.

  4. En D, vous ne pouvez pas lancer par ex. un int. Vous devez lancer un objet héritant de Throwable. Pas de contestation, l'état de choses est meilleur en D, mais bon, c'est une chose que le C++ peut faire que D ne 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: pour mapper naturellement l'encapsulation à la sémantique de protection du système de fichiers, et pour éviter le besoin d'un "ami". Ce choix s'intègre très bien dans la conception globale de modularité de D. Il serait possible de changer les choses pour qu'elles ressemblent davantage au C++, mais cela forcerait les choses; Les choix d'étendue d'encapsulation de C++ sont bons uniquement pour la conception physique de C++.

Il pourrait y avoir une ou deux petites choses, mais dans l'ensemble, cela devrait être le cas.

132
Andrei Alexandrescu

Je pense que vous allez avoir beaucoup de mal à trouver beaucoup en D qui est objectivement pire 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é de mise en œuvre (qui sont généralement dus à la jeunesse du langage et de la mise en œuvre et ont été résolus à une vitesse vertigineuse récemment), ou ce sont des problèmes avec un manque de bibliothèques tierces (qui viendront avec le temps). Le langage lui-même est généralement meilleur que C++, et les cas où C++, en tant que langage, est meilleur vont généralement être là où il y a un compromis où C++ est allé dans un sens et D est allé dans un autre, ou où quelqu'un a des raisons subjectives pour lesquelles ils pense que l'un est meilleur qu'un autre. Mais le nombre de raisons carrément objectives pour lesquelles le C++, en tant que langage, est meilleur est susceptible d'être rare.

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

  1. Parce que D's const est transitif, et parce que le langage a immuable , il a des garanties beaucoup plus fortes que les const de C++, ce qui signifie que D ne pas et ne peut pas avoir mutable. Il ne peut pas avoir const logique . Ainsi, vous obtenez un gain énorme avec le système const de D, mais dans certaines situations, vous ne pouvez tout simplement pas utiliser const comme vous le feriez en C++.

  2. D n'a qu'un seul opérateur de cast, alors que C++ en a 4 (5 si vous comptez l'opérateur de cast C). Cela facilite le traitement des conversions en D dans le cas général, mais est problématique lorsque vous voulez réellement les complications/avantages supplémentaires que const_cast et ses frères fournissent. Mais D est en fait assez puissant pour que vous puissiez utiliser des modèles pour implémenter les transtypages de C++, donc 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 transtypages implicites que C++ et est beaucoup plus susceptible de déclarer que deux fonctions sont en conflit l'une avec l'autre (vous forçant à être plus précis sur les fonctions que vous voulez dire - soit avec des transtypages, soit en donnant le chemin complet du module ). Parfois, cela peut être ennuyeux, mais cela empêche toutes sortes de problèmes de détournement de fonction . Vous savez que vous appelez vraiment la fonction que vous voulez.

  4. Le système de modules de D est beaucoup plus propre que le #include de C++ (sans parler, façon plus rapide à compiler), mais il il manque tout type d'espace de noms au-delà des modules eux-mêmes. Donc, si vous voulez un espace de noms dans un module, vous devez suivre la route Java et utiliser des fonctions statiques sur une classe ou une structure. Cela fonctionne, mais si vous voulez vraiment un espace de noms, ce n'est évidemment pas aussi propre que le véritable espace de noms. Pour la plupart des situations, cependant, l'espace de noms que les modules eux-mêmes vous fournissent est assez (et assez sophistiqué quand il s'agit de choses comme des conflits en fait).

  5. Comme Java et C #, D a un héritage unique plutôt que plusieurs héritages, 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 (et l'héritage multiple de C++ peut devenir très parfois compliqué). Non seulement D a interfaces , mais il a mixins de chaîne , mixins de modèle , et alias this . Ainsi, le résultat final est sans doute plus puissant et n'a pas tous les problèmes que l'héritage multiple de C++ a.

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

  7. Les membres fonctions sur classes sont polymorphes par défaut. Vous ne pouvez pas les déclarer non virtuel . C'est au compilateur de décider s'ils peuvent l'être (ce qui n'est vraiment le cas que s'ils sont final et ne remplacent pas une fonction d'une classe de base). Cela pourrait donc être un problème de performances dans certains cas. Cependant, si vous n'avez vraiment pas besoin du polymorphisme, alors tout ce que vous avez à faire est d'utiliser structs , et ce n'est pas un problème.

  8. D a un garbage collector intégré . Beaucoup de C++ considéreraient cela comme un sérieux inconvénient, et à vrai dire, à l'heure actuelle, sa mise en œuvre pourrait nécessiter un travail sérieux. Il s'améliore, mais ce n'est certainement pas à égalité avec le garbage collector de Java. Cependant, cela est atténué par deux facteurs. Premièrement, si vous utilisez principalement structs et d'autres types de données sur la pile, ce n'est pas un gros problème. Si votre programme n'alloue et ne désalloue pas constamment des trucs sur le tas, ça ira. Et deuxièmement, vous pouvez ignorer le garbage collector si vous le souhaitez et utilisez simplement les malloc et free de C. Il y a quelques fonctionnalités de langage (telles que array slicing ) que vous devrez éviter ou faire attention, et une partie de la bibliothèque standard n'est pas vraiment utilisable sans au moins une certaine utilisation de la [~ # ~] gc [~ # ~] (en particulier le traitement des chaînes), mais vous pouvez écrire en D sans utiliser le garbage collector si vous le voulez vraiment. La chose intelligente à faire est probablement de l'utiliser généralement, puis de l'éviter là où le profilage montre qu'il cause des problèmes de code critique pour les performances, mais vous pouvez l'éviter complètement si vous le souhaitez. Et la qualité de l'implémentation de [~ # ~] gc [~ # ~] s'améliorera avec le temps, éliminant de nombreuses inquiétudes concernant l'utilisation d'un [~ # ~] gc [~ # ~] peut provoquer. Donc, en fin de compte, le [~ # ~] gc [~ # ~] ne sera pas un gros problème, et contrairement à Java, vous pouvez l'éviter si vous le souhaitez.

Il y en a probablement d'autres aussi, mais c'est ce que je peux trouver pour le moment. Et si vous remarquez, ce sont tous des compromis. D a choisi de faire certaines choses différemment de C++ qui ont des avantages certains par rapport à la façon dont C++ les fait, mais 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 pire qu'au début et vous n'aurez plus de problème avec cela une fois que vous vous serez habitué. Si quoi que ce soit, les problèmes dans D vont généralement être nouveaux, causés par de nouvelles choses que d'autres langages n'ont pas faites auparavant ou n'ont pas faites comme D. Dans l'ensemble, D a très bien appris des erreurs de C++.

Et D, en tant que langage, s'améliore par rapport au 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 compilation conditionnelle . C'est l'une des fonctionnalités qui me manquent souvent lorsque je programme en C++. Si C++ l'ajoutait, alors C++ s'améliorerait à pas de géant quand il s'agit de trucs comme des modèles.

  2. D a réflexion à la compilation .

  3. Les variables sont locales au thread par défaut mais peuvent être shared si vous le souhaitez. Cela rend le traitement des threads beaucoup plus propre qu'en C++. Vous avez le contrôle total. Vous pouvez utiliser immutable et passage de message pour communiquer entre les threads, ou vous pouvez créer des variables shared et le faire de 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). Ainsi, la situation de threading de D est 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èle, les messages d'erreur sont bien meilleurs qu'en C++. D rend les modèles 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, et cela peut être très frustrant parfois lors de la programmation en C++.

  5. D a intégré programmation de contrat .

  6. D a un cadre intégré test unitaire .

  7. D a un support intégré pour unicode avec string (UTF-8), wstring (UTF-16) et dstring (UTF-32). Il facilite le traitement de l'unicode. Et si vous souhaitez simplement utiliser string et ne vous inquiétez généralement pas de l'unicode, vous pouvez - même si une certaine compréhension des bases de l'unicode aide avec certaines des fonctions de bibliothèque standard.

  8. D surcharge d'opérateur est beaucoup plus agréable que C++, vous permettant d'utiliser une fonction pour surcharger plusieurs opérateurs en même temps. Un bon exemple de ceci est lorsque vous devez surcharger les opérateurs arithmétiques de base et que leurs implémentations sont identiques sauf pour l'opérateur. Les mixins de cordes 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 les tableaux de C++. Non seulement ils sont d'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 slicing . Et c'est un énorme avantage pour un traitement efficace des tableaux. Les chaînes sont des tableaux de caractères en D, et ce n'est pas un problème (en fait c'est génial!), Parce que les tableaux de D sont si puissants.

Je pourrais continuer encore et encore. De nombreuses améliorations apportées par D sont de petites choses (comme utiliser this pour les noms de constructeur ou interdire si des instructions ou des corps de boucle où un point-virgule est leur corps entier), mais certaines d'entre elles sont assez grandes, et lorsque vous ajoutez-les tous ensemble, cela fait beaucoup une meilleure expérience de programmation. C++ 0x ajoute certaines des fonctionnalités de D dont C++ manquait (par exemple auto et lambdas), mais même avec toutes ses améliorations, il n'y aura toujours pas grand-chose de mieux objectivement sur C++ comme une langue que D.

Il ne fait aucun doute qu'il existe de nombreuses raisons subjectives d'aimer l'une par rapport à l'autre, et l'immaturité relative de la mise en œuvre de D peut parfois être un problème (même si elle s'est améliorée très rapidement récemment - en particulier depuis que les référentiels ont été déplacés vers github ), et le manque de bibliothèques tierces peut certainement être un problème (bien que le fait que D puisse facilement appeler les fonctions C - et dans une moindre mesure, = 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 le langage lui-même. Et comme la qualité des problèmes d'implémentation est corrigée, il deviendra d'autant plus agréable à 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++.

65
Jonathan M Davis

RAII et utilisation de la mémoire de pile

D 2.0 ne permet pas à RAII de se produire sur la pile car il a supprimé la valeur du mot clé scope lors de l'allocation des instances de classe sur la pile.

Vous ne pouvez pas faire d'héritage de type valeur dans D, si efficacement que cela vous oblige à faire une allocation de tas pour n'importe quelle forme de RAII.
Autrement dit, 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 d'utiliser emplace dans D.)

9
user541686

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

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

Notez, par exemple, la verbosité supplémentaire avec template <typename ReturnType, typename... Args> contre (F), Args... contre Args, args... contre args, etc.
Pour le meilleur ou pour le pire, C++ est plus verbeux.

Bien sûr, vous pouvez également 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 cela nécessiterait alors un delegate, alors que l'original accepté any objet appelable. (La version C++ 0x nécessite un std::function objet, donc de toute façon, il est plus verbeux et restrictif dans ses entrées ... ce qui pourrait être bon si vous aimez la verbosité, mauvais si vous ne le faites pas.)

6
user541686

Je ne connais pas grand-chose à D, mais beaucoup, beaucoup de programmeurs C++ que je connais l'aiment beaucoup, et je dois personnellement être d'accord - je n'aime pas l'apparence de D et je n'en prendrai pas de plus près.

Afin de comprendre pourquoi D ne gagne pas plus de traction, vous devez commencer par comprendre ce qui attire les gens vers C++. Dans un mot, la raison numéro un est le contrôle. Lorsque vous programmez en C++, vous avez alors un contrôle total sur votre programme. Vous souhaitez remplacer la bibliothèque Standard? Vous pouvez. Vous voulez effectuer des lancers de pointeurs dangereux? Vous pouvez. Vous voulez violer la constance? Vous pouvez. Vous souhaitez remplacer l'allocateur de mémoire? Vous pouvez. Vous voulez copier autour de la mémoire brute sans égard à son type? Si tu veux vraiment. Vous souhaitez hériter de plusieurs implémentations? Ce sont vos funérailles. Enfer, vous pouvez même obtenir des bibliothèques de récupération de place, comme le collecteur Boehm. Ensuite, vous avez des problèmes comme les performances, qui suivent de près le contrôle - plus le programmeur a de contrôle, plus il peut optimiser son programme. Les performances sont l'une des principales raisons de continuer à utiliser C++.

Voici quelques choses que j'ai vues en faisant une petite recherche et en parlant à quelques personnes qui l'ont essayé:

Hiérarchie de types unifiée. Les utilisateurs C++ utilisent l'héritage très rarement, la plupart des programmeurs C++ préfèrent la composition, et les types ne doivent être liés via l'héritage que s'il y a une très bonne raison de le faire. Le concept d'Objet viole fortement ce principe en liant chaque type. De plus, cela viole l'un des principes les plus élémentaires de 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, sont très fortement opposés à ce que C++ représente en tant que langage en termes de donner au programmeur le contrôle de son programme.

J'ai entendu parler de problèmes avec les fonctions et les délégués. Apparemment, D a à la fois les fonctions et les délégués en tant que types de fonctions appelables au moment de l'exécution, et ce ne sont pas les mêmes, mais ils sont interchangeables ou ... quelque chose ? Mon ami a eu pas mal de problèmes avec eux. Il s'agit certainement d'une rétrogradation de C++, qui a juste std::function et tu as fini.

Ensuite, vous avez la compatibilité. D n'est pas particulièrement compatible avec C++. Je veux dire, aucun langage n'est compatible avec C++, avouons-le, sauf C++/CLI qui est une sorte de tricherie, mais comme barrière à l'entrée, c'est doit être mentionné.

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

printf est l'une des fonctions les plus dangereuses jamais conçues, dans la même famille que les gros problèmes comme gets de l'ancienne bibliothèque C Standard. Si vous le recherchez sur Stack Overflow, vous trouverez de nombreuses questions concernant son utilisation abusive. Fondamentalement, printf est une violation de DRY - vous donnez le type dans la chaîne de format, puis le redonnez lorsque vous donnez c'est un argument. Une violation de DRY où si vous vous trompez, alors de très mauvaises choses se produisent - par exemple, si vous avez changé un typedef d'un entier 16 bits à un entier 32 bits. Il n'est pas non plus extensible imaginez ce qui se passerait si chacun inventait ses propres spécificateurs de format. Les flux iostream de C++ peuvent être lents, et leur choix d'opérateur peut ne pas être le plus grand, et leur interface pourrait utiliser le travail, mais ils sont fondamentalement garantis pour être sûrs, et = DRY n'est pas violé, et ils peuvent être facilement étendus. Ce n'est pas quelque chose qui peut être dit de printf.

Pas d'héritage multiple. C'est très pas la manière C++. Les programmeurs C++ s'attendent à avoir un contrôle complet sur leur programme et le langage imposant ce dont vous ne pouvez pas hériter est une violation de ce principe. De plus, cela rend l'héritage (encore plus) fragile, car si vous changez un type d'une interface en une classe parce que vous voulez fournir une implémentation par défaut ou quelque chose, tout à coup tout le code de votre utilisateur est cassé. Ce n'est pas une bonne chose.

Un autre exemple est string et wstring. En C++, il est déjà assez pénible d'avoir à convertir entre eux, et cette bibliothèque prend-elle en charge Unicode, et cette ancienne bibliothèque C n'utilise que const char*, et avoir à écrire différentes versions de la même fonction selon le type d'argument de chaîne souhaité. Notamment, les en-têtes Windows, par exemple, ont des macros extrêmement irritantes pour faire face au problème qui peut souvent interférer avec votre propre code. Ajouter dstring au mix ne fera qu'empirer les choses, car maintenant au lieu de deux types de chaîne, 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 de logiciels modernes. Il le fait en favorisant des modules interconnectés via des interfaces précises, une fédération de paradigmes de programmation étroitement intégrés, une isolation des threads imposée par le langage, une sécurité de type modulaire, un modèle de mémoire efficace, etc.

L'isolement des threads imposé par la langue n'est pas un plus. Les programmeurs C++ attendent un contrôle total sur leurs programmes, et le langage forçant quelque chose n'est certainement pas ce que le médecin a ordonné.

Je vais également mentionner la manipulation de chaînes 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. Considérez les énormes maux de tête causés par le préprocesseur relativement limité de C, bien connu de tous les programmeurs C++ chevronnés, puis imaginez à quel point cette fonctionnalité sera mal utilisée. La possibilité de créer du code D au moment de la compilation est excellente, mais elle doit être sémantique , pas syntaxique.

De plus, vous pouvez vous attendre à un certain réflexe. D a un garbage collection, que les programmeurs C++ associeront à des langages comme Java et C # qui y sont assez directement opposés dans les philosophies, et les similitudes syntaxiques leur feront penser aussi. Ce n'est pas nécessairement objectivement justifiable, mais c'est quelque chose qui mérite certainement d'être noté.

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 notable.

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

2
DeadMG

La chose la plus importante que C++ "fait mieux" que D est l'interface avec les bibliothèques héritées . Divers moteurs 3D, OpenCL et similaires. Comme D est nouveau, il a beaucoup moins de bibliothèques différentes à choisir.

Une autre différence importante entre le C++ et le D est que le C++ a plusieurs fournisseurs financièrement indépendants, mais en 2014, le D n'en a pratiquement qu'un , le 2 'équipe de l'homme qui l'a créé. Il est intéressant de noter que le "deuxième principe source" qui dit que les projets ne devraient jamais dépendre de la technologie, des composants, qui n'ont qu'un seul fournisseur, semble tenir même pour les logiciels.

A 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 courant de l'ère 2014 [Ruby a été écrit pratiquement à partir de zéro par d'autres personnes . Par conséquent, Ruby peut être considéré comme une langue qui a plus d'un fournisseur financièrement indépendant. D, d'autre part, peut être une technologie géniale, 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, Java, a un bon financier, mais unique, il y a un grand risque que la technologie soit essentiellement abandonnée, quel que soit l'énorme utilisateur de l'entreprise base. Une citation de la Apache Software Foundation , où la CE signifie "Comité Exécutif":

Oracle a fourni à la CE une demande de spécification Java SE 7 et une licence qui se contredisent, restreignent sévèrement la distribution d'implémentations indépendantes de la spécification et, surtout, interdisent la distribution des implémentations open source indépendantes de la spécification

À titre d'historique, on peut dire que les applets Java avaient un canevas 3D accéléré par le matériel des années avant le développement de HTML5 WebGL. Le problème de vitesse de démarrage des applets Java aurait pu être résolu si le Java avait été un projet communautaire, mais les dirigeants du seul financier de Java, Sun Microsystems, l'ont fait. pas jugé suffisamment important pour que l'implémentation Java soit corrigée. Le résultat final: un canevas HTML5 par plusieurs fournisseurs comme une "réplique du pauvre" des frameworks GUI Java (Swing, etc.). Chose intéressante, du côté serveur, le langage de programmation Python présente les mêmes avantages que promis _ Java: écrire une fois, exécuter sur chaque serveur, quel que soit le matériel, à condition que le Python n'est pas compilée en code machine. Le Python est à peu près aussi vieux/jeune que le Java, mais contrairement au Java, il est soutenu par plus d'une équipe de développeurs financée indépendamment (le PyPy et le flux principal Python interprète).

Résumé:

Lors de l'évaluation de la technologie à des fins de 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 plutôt que la technologie T2 dépend des fournisseurs des technologies et si la technologie T1 permet de résoudre les problèmes liés au projet moins cher que T2. Par exemple, si le problème du fournisseur unique était ignoré, alors pour les systèmes d'information, le Java serait une technologie "meilleure" que le C++, car les binaires Java n'ont pas besoin d'être recompilés au déploiement pour le nouveau matériel et la quantité de travail de développement de logiciels liés à la gestion de la mémoire sont plus petits pour le Java que pour le C++. Projets développés à partir de zéro, par ex. les projets qui ne dépendent pas d'autres bibliothèques, pourraient être moins chers à développer en D que C++, mais, d'autre part, le C++ a plus d'un fournisseur et est donc moins risqué à long terme. (L'exemple Java, où Sun Microsystems était presque OK, mais Oracle a pratiquement fait Java le "nouveau COBOL".)

Solution de contournement possible à certaines des limitations C++

L'une des solutions possibles à certaines des limitations du C++ est d'utiliser un modèle de conception, où la tâche initiale est décomposée et les pièces sont "triées" (classées, groupées, divisées, autres-Nice-words-for- la même chose) à 2 classes: contrôle les tâches liées à la logique , où les modèles d'accès à la mémoire ne permettent pas la localité; tâches de type traitement du signal , où la localisation est facilement réalisée. Les tâches "similaires" de traitement du signal peuvent être implémentées comme un ensemble de programmes de console relativement simplistes. Les tâches liées à la logique de contrôle sont placées toutes dans un seul script écrit en Ruby ou Python ou autre chose, où le confort de développement logiciel est plus prioritaire que la vitesse.

Pour éviter l'initialisation coûteuse des processus du système d'exploitation et la copie de données entre les processus du système d'exploitation, l'ensemble de petites applications de console C++ peut être implémenté comme un seul programme C++ qui est démarré en tant que "servlet" par Ruby/Python/etc. scénario. Ruby/Python/etc. le script arrête le 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 à être facilement divisée en morceaux qui peuvent être classés dans les 2 classes susmentionnées, alors une chose à essayer pourrait être de reformuler le problème afin que la tâche initiale change.

1
Martin Vahi

Une chose que j'apprécie en C++ est la possibilité de documenter un argument de fonction ou une valeur de retour en tant que référence C++ au lieu d'un pointeur, ce qui implique de prendre une valeur nonnull.

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 transformant A en D struct et en marquant l'argument foo()- comme ref (les classes sont les types de référence et les structures sont des types de valeur en D, similaires à C #).

Je pense qu'il existe un plan pour créer un modèle NonNullable pour les classes en tant que construction de bibliothèque standard D à la place. Malgré cela, j'aime la brièveté de seulement Type& Par rapport à NonNullable(Type), et je préférerais que non nullable comme valeur par défaut (en rendant quelque chose comme Type et Nullable(Type)). Mais il est trop tard pour changer cela pour D et je dérive maintenant hors sujet.

1
lumor