Что С ++ делает лучше, чем D?

135

Недавно я изучал D и начинаю знакомиться с языком. Я знаю, что он предлагает, я еще не знаю, как все использовать, и я не знаю много о D идиомах и так далее, но я учусь.

Мне нравится D. Это хороший язык, который в некотором роде является огромным обновлением C, и все сделано хорошо. Ни одна из функций не кажется «прикрученной», но на самом деле достаточно хорошо продумана и продумана.

Вы часто будете слышать, что D - это то, чем должен был быть C ++ (я оставляю вопрос, верно ли это для каждого и каждого, чтобы решать самим, чтобы избежать ненужных пламенных войн). Я также слышал от нескольких программистов на C ++, что им нравится D гораздо больше, чем C ++.

Сам я, хотя знаю C, не могу сказать, что знаю C ++. Я хотел бы услышать от кого-то, кто знает и C ++, и D, если они думают, что есть что-то, что C ++ делает лучше, чем D как язык (имеется в виду не обычное «у него больше сторонних библиотек» или «там больше ресурсов» или « существует больше заданий, требующих C ++, чем D ").

D был разработан некоторыми очень опытными программистами C ++ ( Уолтером Брайтом и Андреем Александреску , с помощью сообщества D), чтобы исправить многие проблемы, которые имел C ++, но было ли что-то, что на самом деле не улучшалось в конце концов? Что-то он пропустил? Что-то, что вы думаете, не было лучшим решением?

Также обратите внимание, что я говорю о D 2.0 , а не D 1.0 .

Анто
источник
15
Я убедился, что сообщество D действительно видит это, так как я уверен, что здесь гораздо больше разработчиков C ++, чем разработчиков D ++. Таким образом, у вас будут более интересные или хотя бы разнообразные ответы.
Klaim
7
Кроме того, D2 был разработан Уолтером Брайтом, но с Александреску тоже. Вы можете исправить это в своем вопросе.
Klaim
2
@Klaim: в D и стандартной библиотеке было (и все еще есть) большое участие сообщества.
Михал Минич
28
@Anto Как язык, C ++ намного лучше, чем D, заставляя вас, программиста, ненавидеть свою жизнь.
Арлен
6
@jokoon: На самом деле, да, с очень небольшой работой: digitalmars.com/d/2.0/interfaceToC.html
Anto

Ответы:

124

Большинство вещей, которые C ++ «делает» лучше, чем D, являются мета-вещами: у C ++ есть лучшие компиляторы, лучшие инструменты, более зрелые библиотеки, больше привязок, больше экспертов, больше учебников и т. Д. В основном у него есть все больше и больше внешних вещей, которые вы можно ожидать от более зрелого языка. Это неоспоримо.

Что касается самого языка, на мой взгляд, есть несколько вещей, которые C ++ делает лучше, чем D. Возможно, есть и другие, но вот некоторые из них, которые я могу перечислить в верхней части моей головы:

C ++ имеет более продуманную систему типов.
В настоящее время в системе типов D существует довольно много проблем, которые, по-видимому, являются упущениями в дизайне. Например, в настоящее время невозможно скопировать структуру const в неконстантную структуру, если структура содержит ссылки на объекты класса или указатели из-за транзитивности const и того, как конструкторы postblit работают с типами значений. Андрей говорит, что знает, как решить эту проблему, но не дал никаких подробностей. Эта проблема, безусловно, решаема (одним из решений было бы введение конструкторов копирования в стиле C ++), но в настоящее время она является основной проблемой в языке.

Еще одна проблема, которая меня беспокоила, - это отсутствие логической константы (то есть не mutableтакой, как в C ++). Это отлично подходит для написания поточно-ориентированного кода, но затрудняет (невозможно?) Ленивую инициализацию внутри объектов const (вспомним функцию const get ', которая создает и кэширует возвращаемое значение при первом вызове).

Наконец, учитывая эти существующие проблемы, я беспокоюсь о том , как остальная часть системы типа ( pure, sharedи т.д.) будут взаимодействовать со всем остальным на языке , когда они ставятся использовать. Стандартная библиотека (Фобос) в настоящее время очень мало использует продвинутую систему типов D, поэтому я считаю разумным вопрос, выдержит ли она стрессовую нагрузку. Я скептически, но оптимистично.

Обратите внимание, что в C ++ есть некоторые проблемы с системой типов (например, нетранзитивный const, требующий, iteratorа также const_iterator), которые делают его довольно уродливым, но хотя система типов C ++ немного ошибочна по частям, она не мешает вам выполнять работу, как D иногда делает.

Изменить: Чтобы уточнить, я считаю, что C ++ имеет более продуманную систему типов - не обязательно лучше - если это имеет смысл. По сути, в DI чувствуют, что существует риск, связанный с использованием всех аспектов системы типов, которых нет в C ++.

D иногда слишком удобен.
Одна критика, которую вы часто слышите о C ++, заключается в том, что он скрывает от вас некоторые низкоуровневые проблемы, например, простые присваивания, такие как a = b;выполнение многих действий, таких как вызов операторов преобразования, вызов операторов перегрузки и т. Д., Которые могут быть трудно увидеть из кода. Некоторым это нравится, другим нет. В любом случае, в D это хуже (лучше?) Из - за такие вещи , как opDispatch, @property, opApply, lazyкоторые имеют потенциал , чтобы изменить невинно глядя код на вещи , которые вы не ожидаете.

Я не думаю, что это большая проблема лично, но некоторые могут найти это отталкивающим.

D требует сборки мусора
Это можно рассматривать как спорный , потому что можно запустить D без GC. Однако то, что это возможно, не означает, что это практично. Без GC вы потеряете много функций D, и использование стандартной библиотеки было бы похоже на хождение по минному полю (кто знает, какие функции выделяют память?). Лично я считаю, что использовать D без GC совершенно нецелесообразно, и если вы не являетесь поклонником GC (как и я), то это может быть довольно неприятным.

Наивные определения массивов в D выделяют память.
Это моя любимая мозоль:

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

По-видимому, чтобы избежать выделения в D, вы должны сделать:

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

Эти небольшие распределения «за вашей спиной» являются хорошими примерами моих предыдущих двух пунктов.

Изменить: Обратите внимание, что это известная проблема, над которой работает.
Изменить: теперь это исправлено. Распределение не происходит.

Заключение
Я сфокусировался на недостатках D против C ++, потому что это то, что задал вопрос, но, пожалуйста, не рассматривайте этот пост как утверждение, что C ++ лучше, чем D. Я мог бы легко сделать больший пост из мест, где D лучше чем С ++. Вам решать, какой из них использовать.

Петр Александр
источник
Я посмотрел на D несколько лет назад (до 2.0). Сборка мусора тогда действительно не требовалась - она ​​была там по умолчанию, но вы можете отказаться от низкоуровневого кода. Единственное, в чем я ошибся, так это в том, что я не смог найти ответ. Например, в древовидном контейнере код библиотеки может управлять памятью для самих узлов дерева. Дерево в целом все еще можно собирать, а IIRC - деструктор, который собирает все эти узлы. Но объекты, на которые ссылаются данные в этом контейнере, также должны быть коллекционируемыми - должен быть хук, чтобы пометить все элементы данных в дереве для GC.
Steve314
3
Вы все еще можете отключить GC для низкоуровневого кода - Питер говорит, что язык в настоящее время во многом зависит от него. Кроме того, вы можете указать GC сканировать диапазоны за пределами его управляемой кучи, используя его API: GC.addRange из core.memory .
Владимир Пантелеев
+1 за указание на то, что стандартная библиотека D является сборщиком мусора и что код GC-off не является бесшовным взаимодействием. Это не то, что я думал об этом, но это кажется серьезным препятствием для преодоления.
масон
132

Когда я присоединился к D development, я был одним из тех, кто знает больше всего о C ++. Теперь я нахожусь в еще более необычном положении, чтобы быть одним из тех людей, которые знают больше всего о D. Я говорю это не для того, чтобы присвоить кредит или похвастаться правами настолько, чтобы заметить, что я с любопытством выгодная позиция для ответа на этот вопрос. То же самое относится и к Уолтеру.

В общем, вопрос о том, что C ++ (и под этим я имею в виду C ++ 2011) лучше, чем D, столь же противоречив, как и вопрос: «Если вы заплатите профессионалу за уборку дома, из каких мест они уйдут? грязнее, чем раньше? Что бы ни значило то, что C ++ мог сделать, а D не смог, он всегда показался мне больным большим пальцем и Уолтеру, поэтому почти по определению нет ничего, что C ++ может сделать, чего бы не было в пределах досягаемости D.

Одна вещь, которая редко понимается в языковом дизайне (потому что немногим людям повезло на самом деле сделать это), заключается в том, что количество несанкционированных ошибок намного меньше, чем может показаться. Многие из нас, пользователей языка, смотрят на ту или иную конструкцию и говорят: «Э-э! Это так неправильно! О чем они думали?» Дело в том, что большинство неуклюжих случаев в языке являются следствием нескольких фундаментальных решений, которые все разумны и желательны, но принципиально конкурируют или противоречат друг другу (например, модульность и эффективность, краткость и контроль и т. Д.).

Имея это в виду, я перечислю несколько вещей, о которых могу подумать, и для каждого я объясню, как выбор D обусловлен желанием выполнить какой-то другой, более высокий уровень, устав.

  1. D предполагает, что все объекты могут быть перемещены путем побитового копирования. Это оставляет меньшую часть проектов для C ++, особенно те, которые используют внутренние указатели, то есть класс, содержащий указатели внутри себя. (Любой такой дизайн может быть переведен без каких-либо или незначительных затрат на эффективность в D, но это потребует усилий по переводу.) Мы приняли это решение, чтобы значительно упростить язык, сделать копирование объектов более эффективным без или с минимальным вмешательством пользователя и избежать вся копия конструкции болота и rvalue ссылки показывают в целом.

  2. D запрещает типы неоднозначного пола (которые не могут решить, являются ли они типами значения или ссылочными). Такие конструкции единодушно избегают в C ++ и почти всегда ошибочны, но некоторые из них технически правильны. Мы сделали этот выбор, потому что он запрещает в основном некорректный код и только небольшой фрагмент правильного кода, который можно изменить. Мы считаем, что это хороший компромисс.

  3. D запрещает многокорневые иерархии. Предыдущий постер здесь был очень взволнован этой конкретной темой, но это довольно проторенная почва, и нет очевидного преимущества иерархии без корней по сравнению с иерархиями, которые имеют общий корень.

  4. В D вы не можете бросить, например, Int. Вы должны бросить объект, наследующий Throwable. Нет сомнений в том, что положение дел лучше в D, но, в общем-то, C ++ может делать то, чего не может D.

  5. В C ++ модуль инкапсуляции - это класс. В D это модуль (т.е. файл). Уолтер принял это решение по двум причинам: естественным образом сопоставить инкапсуляцию с семантикой защиты файловой системы и устранить потребность в «друге». Этот выбор очень хорошо интегрируется в общую модульную конструкцию D. Можно было бы изменить вещи, чтобы они были больше похожи на C ++, но это заставило бы вещи; Выбор области инкапсуляции в C ++ подходит только для физического проектирования C ++.

Может быть одна или две вещи поменьше, но в целом так и должно быть.

Андрей Александреску
источник
6
@DeadMG: чтобы это работало в C ++, перемещаемому объекту потребуется обратный указатель на объект, указывающий на него (чтобы он мог обновляться во время создания копии). Если это так, в D вы можете использовать конструктор postblit для обновления указателя в любом случае. Пожалуйста, не спорьте с D, если у вас есть только мимолетное знание об этом.
Питер Александр
13
@Peter: Это должна быть ссылка, хотя срок ее службы строго основан на области? Я должен тратить накладные расходы на его динамическое выделение, а также на косвенные издержки, накладные расходы на кеш и сбор, потому что я хочу присвоить ему псевдоним? Кроме того, я надеюсь, что сборщик может собрать его детерминистически, для эквивалентной семантики. Это совершенно очевидно, не контролировать.
DeadMG
3
@sbi: Существование высшего класса никак не влияет на ваш выбор. В решетке типа класса всегда есть верх и низ. Дно явно только на нескольких языках . Вершина (то есть Объект и т. Д.) Является явной в большем количестве языков. Эти типы всегда существуют в концепции; когда они также доступны, они просто предлагают пользователю несколько дополнительных возможностей, не создавая проблем.
Андрей Александреску
6
@quant_dev: вы будете рады услышать, что проект GSoC уже в хорошей форме и сфокусирован на высокопроизводительной линейной алгебре с использованием BLAS. Он также предоставляет «наивные» реализации соответствующих примитивов для целей тестирования и тестирования. Чтобы ответить на ваш второй вопрос, Java устанавливает довольно низкую планку для сравнения числовых библиотек. У него всегда будет проблема преодоления барьера JNI для доступа к высокопроизводительным библиотекам алгебры, и синтаксис будет плохим, поскольку в Java отсутствует перегрузка операторов.
Андрей Александреску
4
@PeterAlexander: DeadMG прямо на месте. «Вы не должны использовать указатели для типов значений» явно игнорирует тот факт, что указатели на любом языке обычно используются с типами значений (вы действительно ожидаете увидеть столь Object*же широко используемый как int*?), И D, кажется, полностью игнорирует снижение производительности или утверждение, что оно не существует. Это, очевидно, неверно - пропадание кэша во многих случаях весьма заметно, поэтому C ++ всегда будет иметь такое преимущество в гибкости по сравнению с D.
Mehrdad
65

Я думаю , что вы будете иметь очень трудно найти много D , которая объективнохуже чем С ++. Большинство проблем с D, когда вы можете объективно сказать, что это хуже, это либо проблемы с качеством реализации (которые, как правило, связаны с тем, насколько молоды язык и реализация, и которые в последнее время были исправлены с головокружительной скоростью), либо проблемы с отсутствием сторонних библиотек (что будет со временем). Сам язык, как правило, лучше, чем C ++, и случаи, когда C ++, как язык, лучше, обычно либо идут туда, где есть компромисс, когда C ++ идет одним путем, а D - другим, либо когда у кого-то есть субъективные причины, почему они думаю, что одно лучше другого. Но число явных объективных причин, почему C ++, как язык, лучше, скорее всего, будет немногочисленным.

На самом деле, я должен по-настоящему взбесить свой мозг, чтобы найти причины, по которым C ++ как язык лучше, чем D. Что обычно приходит на ум, это вопрос компромиссов.

  1. Поскольку const у D транзитивен, а язык неизменен , у него гораздо более сильные гарантии, чем у C ++ const, что означает, что D не имеет и не может иметь mutable. Это не может иметь логическое постоянство . Таким образом, вы получаете огромный выигрыш с помощью const-системы D, но в некоторых ситуациях вы просто не можете использовать, constкак в C ++.

  2. D имеет только один оператор приведения, в то время как C ++ имеет 4 (5, если считать оператор C). Это облегчает работу с приведениями в D в общем случае, но проблематично, когда вам действительно нужны дополнительные сложности / преимущества, которые const_castпредоставляют его братья. Но D на самом деле достаточно мощный, чтобы вы могли использовать шаблоны для реализации приведений C ++, поэтому, если вы действительно хотите их получить, вы можете их иметь (и они могут даже оказаться в стандартной библиотеке D в какой-то момент).

  3. У D гораздо меньше неявных приведений, чем в C ++, и с гораздо большей вероятностью заявляют, что две функции находятся в конфликте друг с другом (что заставляет вас более точно определять, какую из функций вы имеете в виду - либо с приведением, либо путем указания полного пути к модулю ). Иногда это может раздражать, но это предотвращает все виды перехватов функций . Вы знаете, что вы действительно вызываете функцию, для которой вы имеете в виду.

  4. Модульная система D намного чище, чем #include C ++ (не говоря уже о том, что при компиляции это происходит намного быстрее), но ей не хватает пространства имен, кроме самих модулей. Итак, если вам нужно пространство имен в модуле, вы должны пройти по маршруту Java и использовать статические функции для класса или структуры. Это работает, но если вам действительно нужно пространство имен, оно явно не такое чистое, как реальное пространство имен. Однако для большинства ситуаций пространство имен, которое предоставляют вам сами модули, достаточно (и довольно сложно, когда дело касается конфликтов).

  5. Подобно Java и C #, D имеет одиночное наследование, а не множественное наследование, но в отличие от Java и C #, это дает вам несколько фантастических способов получить тот же эффект без всех проблем, которые имеет множественное наследование C ++ (а множественное наследование C ++ может стать очень запутанным во время). У D есть не только интерфейсы , но и строковые миксины , шаблонные миксины и псевдоним this . Итак, конечный результат, возможно, более мощный и не имеет всех проблем, которые делает множественное наследование C ++.

  6. Подобно C #, D разделяет структуры и классы . Классы являются ссылочными типами, которые имеют наследование и являются производными Object, тогда как структуры являются типами значений без наследования. Это разделение может быть как хорошим, так и плохим. Это избавляет от классической проблемы среза в C ++ и помогает отделить типы, которые действительно являются типами значений, от типов, которые должны быть полиморфными, но сначала, по крайней мере, различие может раздражать программиста на C ++. В конечном счете, у него есть ряд преимуществ, но он заставляет вас относиться к своим типам несколько иначе.

  7. Члены функции из классов полиморфны по умолчанию. Вы не можете объявить их не виртуальными . Компилятор должен решить, могут ли они быть (что действительно только в том случае, если они являются окончательными и не переопределяют функцию из базового класса). Так что это может быть проблемой производительности в некоторых случаях. Однако, если вам действительно не нужен полиморфизм, тогда все, что вам нужно сделать, это использовать структуры , и это не проблема.

  8. D имеет встроенный сборщик мусора . Многие из C ++ сочли бы это серьезным недостатком, и, по правде говоря, в настоящее время для его реализации может потребоваться серьезная работа. Он улучшается, но он определенно не на уровне сборщика мусора в Java. Однако это смягчается двумя факторами. Во-первых, если вы в основном используете структуры и другие типы данных в стеке, то это не большая проблема. Если ваша программа не постоянно распределяет и освобождает вещи в куче, все будет хорошо. И во-вторых, вы можете пропустить сборщик мусора, если хотите, и просто использовать C mallocи free. Есть некоторые языковые возможности (например, нарезка массивов), которых вам следует избегать или соблюдать осторожность, и некоторая стандартная библиотека на самом деле не пригодна для использования, по крайней мере, без некоторого использования GC (особенно при обработке строк), но вы можете писать в D без использования сборщика мусора, если Вы действительно хотите. Самое разумное, что нужно сделать, - это, как правило, использовать его, а затем избегать, когда профилирование показывает, что оно создает проблемы для критичного к производительности кода, но вы можете полностью избежать этого, если хотите. И качество реализации GC со временем улучшится, устраняя многие проблемы, которые может вызвать использование GC . Так что, в конечном счете, сборщик мусора не будет такой большой проблемой, и в отличие от Java, вы можете избежать этого, если захотите.

Возможно, есть и другие, но это то, что я могу придумать в данный момент. И если вы заметите, они все компромиссы. D решил делать некоторые вещи, отличные от C ++, которые имеют определенные преимущества перед C ++, но также имеют некоторые недостатки. Что лучше, зависит от того, что вы делаете, и во многих случаях, вероятно, поначалу будет только хуже, и тогда у вас не будет проблем с этим, как только вы к этому привыкнете. Во всяком случае, проблемы в D, как правило, будут новыми, вызванными новыми вещами, которые другие языки не делали раньше или делали не совсем так, как D. В целом, D очень многому научился на ошибках C ++.

И D, как язык, улучшается по сравнению с C ++ во многих отношениях, и я думаю, что в общем случае D объективно лучше.

  1. D имеет условную компиляцию . Это одна из тех функций, которые мне часто не хватает при программировании на C ++. Если C ++ добавит его, то C ++ будет стремительно улучшаться, когда дело доходит до таких вещей, как шаблоны.

  2. D имеет отражение во время компиляции .

  3. Переменные по умолчанию являются локальными для потока, но могут быть, sharedесли вы хотите, чтобы они были. Это делает работу с потоками намного чище, чем в C ++. Вы в полном контроле. Вы можете использовать immutableи передачу сообщений для связи между потоками, или вы можете создавать переменные sharedи делать это C ++ способом с мьютексами и условными переменными. Даже это улучшено по сравнению с C ++ с введением синхронизированных (аналогично C # и Java). Итак, ситуация с потоками в D гораздо лучше, чем в C ++.

  4. Шаблоны D гораздо более мощные, чем шаблоны C ++, что позволяет вам делать гораздо больше, гораздо проще. А с добавлением шаблонных ограничений сообщения об ошибках стали намного лучше, чем в C ++. D делает шаблоны очень мощными и удобными в использовании. Не случайно, что автор Modern C ++ Design является одним из главных соавторов D. Я считаю, что C ++-шаблонам по-настоящему не хватает по сравнению с D-шаблонами, и это может быть очень неприятно при программировании на C ++.

  5. У D есть встроенное программирование контракта .

  6. D имеет встроенную систему модульного тестирования .

  7. D имеет встроенную поддержку Unicode с string(UTF-8), wstring(UTF-16), и dstring(UTF-32). Это позволяет легко иметь дело с юникодом. И если вы хотите просто использовать stringЮникод и вообще не беспокоиться о нем, вы можете это сделать, хотя некоторое понимание основ юникода помогает с некоторыми стандартными библиотечными функциями.

  8. Перегрузка операторов в D намного лучше, чем в C ++, что позволяет вам использовать одну функцию для перегрузки нескольких операторов одновременно. Главный пример этого - когда вам нужно перегрузить основные арифметические операторы, и их реализации идентичны, за исключением оператора. Струнные миксины делают это быстрым, позволяя вам иметь одно простое определение функции для них всех.

  9. Массивы D значительно лучше, чем массивы C ++. Они не только правильного типа с длиной, но они могут быть добавлены и изменены. Объединить их легко. И лучше всего, у них есть нарезка . И это огромное преимущество для эффективной обработки массивов. Строки - это массивы символов в D, и это не проблема (на самом деле это здорово!), Потому что массивы D настолько мощные.

Я мог бы продолжать и продолжать. Многие улучшения, которые обеспечивает D, - это мелочи (например, использование thisдля имен конструкторов или запрет на использование операторов или тел цикла, где точка с запятой - это все их тело), ​​но некоторые из них довольно большие, и когда вы добавляете все это вместе, делает для гораздо лучшего опыта программирования. C ++ 0x действительно добавляет некоторые функции, которыми обладает D, которых не было в C ++ (например, autoлямбда-выражения), но даже со всеми его улучшениями все равно не будет много чего, что объективно лучше в отношении C ++ как языка, чем D.

Нет никаких сомнений в том, что существует множество субъективных причин для того, чтобы понравиться друг другу, и относительная незрелость реализации D иногда может быть проблемой (хотя в последнее время она очень быстро улучшается, особенно после того, как репозитории были перемещены в github ). и отсутствие сторонних библиотек определенно может быть проблемой (хотя тот факт, что D может легко вызывать функции C - и, в меньшей степени, функции C ++ - определенно смягчает проблему). Но это проблемы качества реализации, а не проблемы с самим языком. И поскольку проблемы с качеством реализации исправлены, становится гораздо приятнее пользоваться D.

Итак, я полагаю, что короткий ответ на этот вопрос «очень мало». D, как язык, как правило, превосходит C ++.

Джонатан М Дэвис
источник
2
Языки сборки мусора используют в 2-5 раз больше памяти, чем не-GC (согласно докладу Александреску о YT), так что это определенно проблема, если это (использование mem) является узким местом.
NoSenseEtAl
9

RAII и использование памяти стека

D 2.0 не позволяет RAII происходить в стеке, потому что он удалил значение scopeключевого слова при распределении экземпляров классов в стеке.

Вы не можете делать наследование типа значения в D, настолько эффективно, что вы заставляете вас делать выделение кучи для любой формы RAII.
То есть, если вы не используете emplace, но это очень болезненно, поскольку вы должны распределять память вручную. (Я еще не нашел его практичным для использования emplaceв D.)

Mehrdad
источник
6

C ++ намного лучше заставляет вас быть многословным. Это может быть лучше или хуже в ваших глазах, в зависимости от того, нравится ли вам вывод или многословие.

Сравните памятки во время выполнения в 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...);
    });
}

с тем же самым в 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));
    };
}

Обратите внимание, например, на дополнительную многословность с « template <typename ReturnType, typename... Args>против» (F), « Args...против» Args, « args...против» argsи т. Д.
К лучшему или к худшему, C ++ более многословен.

Конечно, вы также можете сделать это в 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));
        };
    }
}

и они будут выглядеть почти одинаково, но тогда для этого потребуется a delegate, тогда как оригинал принял любой вызываемый объект. (Для версии C ++ 0x требуетсяstd::function объект, так что в любом случае он более многословен и ограничен во входных данных ... что может быть хорошо, если вам нравится многословие, и плохо, если вы этого не делаете.)

Mehrdad
источник
2

Я не очень разбираюсь в D, но многим, многим знакомым мне программистам на С ++ это очень не нравится, и я лично должен согласиться - мне не нравится внешний вид D, и я не буду подходить ближе.

Чтобы понять, почему D не набирает обороты, нужно начать с понимания того, что привлекает людей в C ++. Одним словом, причина номер один - контроль. Когда вы программируете на C ++, вы полностью контролируете свою программу. Хотите заменить стандартную библиотеку? Ты можешь. Хотите делать небезопасные приведения указателей? Ты можешь. Хотите нарушить const-правильность? Ты можешь. Хотите заменить распределитель памяти? Ты можешь. Хотите копировать в сырую память, не обращая внимания на ее тип? Если вы действительно хотите. Хотите наследовать от нескольких реализаций? Это твои похороны. Черт, вы даже можете получить библиотеки для сбора мусора, такие как сборщик Boehm. Тогда возникают такие проблемы, как производительность, которая тесно связана с управлением - чем больше у программиста контроля, тем более оптимизированным он может выполнять свою программу.

Вот несколько вещей, которые я видел, когда проводил небольшое исследование и разговаривал с парой людей, которые попробовали это:

Единая иерархия типов. Пользователи C ++ используют наследование очень редко, большинство программистов на C ++ предпочитают композицию, и типы следует связывать через наследование только в том случае, если для этого есть очень веская причина. Концепция объекта сильно нарушает этот принцип, связывая каждый тип. Кроме того, это нарушает один из самых основных принципов C ++ - вы используете только то, что хотите. Отсутствие выбора в отношении наследования от Object и сопутствующих ему затрат очень сильно противоречит тому, что C ++ обозначает как язык с точки зрения предоставления программисту контроля над своей программой.

Я слышал о проблемах с функциями и делегатами. Очевидно, D имеет функции и делегаты как типы функций, вызываемые во время выполнения, и они не одинаковы, но они взаимозаменяемы или ... что-то? У моего друга было немало проблем с ними. Это, безусловно, понижение C ++, который только что std::functionи все готово.

Тогда у вас есть совместимость. D не особенно совместим с C ++. Я имею в виду, что ни один язык не совместим с C ++, давайте посмотрим правде в глаза, за исключением C ++ / CLI, который является своего рода обманом, но в качестве барьера для входа следует упомянуть.

Затем есть некоторые другие вещи. Например, просто прочитайте статью в Википедии.

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

printfявляется одной из самых небезопасных функций, когда-либо созданных, в той же семье, что и большие проблемы, как getsв старой библиотеке C Standard. Если вы будете искать его в Stack Overflow, вы найдете множество вопросов, касающихся его неправильного использования. По сути, printfэто нарушение СУХОЙ- вы даете тип в строке формата, а затем даете его снова, когда вы даете ему аргумент. Нарушение DRY, когда если вы ошиблись, то произойдут очень плохие вещи, скажем, если вы изменили typedef с 16-битного целого на 32-битное. Это также вообще не расширяемо - представьте, что произойдет, если все придумают свои собственные спецификаторы формата. Iostream C ++ может быть медленным, и их выбор оператора может быть не самым большим, и их интерфейс мог бы использовать работу, но они в принципе гарантированно безопасны, и DRY не нарушается, и они могут быть легко расширены. Об этом нельзя сказать printf.

Нет множественного наследования. Это совсем не так, как в C ++. Программисты C ++ ожидают полного контроля над своей программой, и язык, обеспечивающий то, от чего вы не можете унаследовать, является нарушением этого принципа. Кроме того, это делает наследование (даже более) хрупким, потому что если вы измените тип с интерфейса на класс, потому что вы хотите предоставить реализацию по умолчанию или что-то еще, внезапно весь код вашего пользователя будет нарушен. Это не очень хорошая вещь.

Еще один пример stringи wstring. В C ++ уже довольно болезненно выполнять преобразования между ними, и поддерживает ли эта библиотека Unicode, и эта старая библиотека C использует только ее const char*, и ей приходится писать разные версии одной и той же функции в зависимости от желаемого типа строкового аргумента. В частности, заголовки Windows, например, имеют несколько чрезвычайно раздражающих макросов, чтобы справиться с проблемой, которая часто может мешать вашему собственному коду. Добавление dstringк миксу только усугубит ситуацию, так как теперь вместо двух типов строк вам нужно управлять тремя. Наличие более одного строкового типа увеличит затраты на обслуживание и представит повторяющийся код, работающий со строками.

Скотт Мейерс пишет:

D - это язык программирования, созданный для помощи программистам в решении задач современной разработки программного обеспечения. Это достигается за счет поддержки модулей, соединенных через точные интерфейсы, объединения тесно интегрированных парадигм программирования, языковой изоляции потоков, безопасности модульного типа, эффективной модели памяти и многого другого.

Языковая изоляция потоков не является плюсом. Программисты на C ++ ожидают полного контроля над своими программами, и язык, навязывающий что-либо, определенно не тот, что предписал доктор.

Я также собираюсь упомянуть манипуляции со строками во время компиляции. D имеет возможность интерпретировать код D во время компиляции. Это не плюс. Подумайте об огромных головных болях, вызванных относительно ограниченным препроцессором C, хорошо известным всем опытным программистам на C ++, а затем представьте, насколько сильно эта функция будет злоупотреблена. Возможность создавать D-код во время компиляции великолепна, но она должна быть семантической , а не синтаксической.

Кроме того, вы можете ожидать определенный рефлекс. У D есть сборка мусора, которую программисты на C ++ будут ассоциировать с такими языками, как Java и C #, которые в философии довольно прямо ей противостоят, и синтаксическое сходство напомнит им об этом. Это не обязательно объективно оправдано, но это то, что, безусловно, следует отметить.

По сути, он не предлагает так много, что программисты C ++ уже не могут сделать. Может быть, проще написать факториальную метапрограмму в D, но мы уже можем написать факториальную метапрограмму в C ++. Возможно, в D вы можете написать трассировщик лучей во время компиляции, но в действительности никто не хочет этого делать. По сравнению с фундаментальными нарушениями философии C ++, то, что вы можете сделать в D, не особенно заметно.

Даже если на первый взгляд это всего лишь проблемы, я уверен, что тот факт, что D на самом деле совсем не похож на C ++, вероятно, является хорошей причиной того, что многие программисты на C ++ не переходят на D. Возможно, D нужно сделать лучшую работу, рекламируя себя.

DeadMG
источник
9
@DeadMG: Это на 100% неверно и упускает смысл сказать, что это определенно понижениеstd::function версии C ++, которое просто есть, и все готово. Почему? Потому что у вас, например, также есть указатели на функции. Это точно то же самое в D: «функции» D являются указателями на функции, а «делегаты» D такие же, как в С ++ std::function(за исключением того, что они встроены). Нет никакого «понижения» где бы то ни было - и между ними есть соответствие 1: 1, так что это не должно сбивать с толку, если вы знакомы с C ++.
Мердад
10
@Mark Trapp: Должен признать, что я не совсем понимаю вашу позицию по этой теме - комментарии не будут использоваться, вы знаете, комментируя ответ?
klickverbot
6
@Mark Trapp: Моя точка зрения заключается в том, что большинство комментариев здесь не устарели (мета-обсуждение, которое вы связали, особенно относится к предложениям, которые уже были включены в исходный пост), но указало на фактические неточности в посте, которые все еще присутствуют ,
klickverbot
7
Примечание к формату: функция форматирования D безопасна от типов (решает проблемы безопасности / переполнения) и не нарушает DRY, поскольку строка формата определяет только форматирование аргументов, а не их типы. Это возможно благодаря типичной безопасности D. Следовательно, это возражение, по крайней мере, полностью недействительно.
Джастин W
17
@Mark: Какой бы ни была текущая политика в отношении них, я считаю это глупым и мешающим обсуждению комментариев удаляться . Я думаю, что у этого ответа были обширные обсуждения (которые меня сейчас интересуют), но я не уверен, и у меня нет никакого способа узнать. В той комнате, с которой вы связаны, более 10 тысяч сообщений, и у меня нет шансов найти обсуждение, которое, как мне кажется, я помню, имело место, но не могу вспомнить содержание. Обсуждения, касающиеся этого ответа, относятся к этому ответу , а не к какому-либо чату, где они могут быть смешаны в обсуждениях секса, наркотиков и рок-н-ролла.
ВОО
1

Одна вещь, которую я ценю в C ++, - это возможность документировать аргумент функции или возвращаемое значение в виде ссылки на C ++ вместо указателя, что подразумевает принятие не nullзначения.

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

Версия 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;
}

Чтобы быть справедливым, вы можете очень близко подойти к C ++, превратив Aв D structи пометив foo()-argument как ref(классы являются ссылочными типами, а структуры являются типами значений в D, аналогично C #).

Я полагаю, что существует план создания NonNullableшаблона для классов в качестве стандартной библиотеки D. Несмотря на это, мне нравится краткость по Type&сравнению с NonNullable(Type), и я предпочитаю non-nullable в качестве значения по умолчанию (рендеринг чего-то вроде Typeи Nullable(Type)). Но уже слишком поздно, чтобы изменить это для D, и я сейчас отвлекаюсь от темы.

lumor
источник
3
И аргументы функций, и возвращаемые значения в D могут быть помечены, refчтобы дать вам тот же эффект, что и в C ++ &. Единственное существенное отличие состоит в том ref, что временное решение не займет, даже если оно будет const.
Джонатан М Дэвис
Мне нравится, что возврат ссылок на локальные переменные в D запрещен, я не знал об этом, пока не прочитал ваш комментарий. Но вы все равно можете вернуть нелокальную нулевую ссылку, не думая об этом так, чтобы оператор разыменования Си заставил вас задуматься.
lumor
Вы путаете вещи. Классы всегда являются ссылками, и это отдельно от ссылки. Ссылки в D похожи на ссылки в Java. Они управляемые указатели. Передача или возврат по ref - это все равно, что передать или вернуть с помощью & в C ++. Передача ссылки на класс по ref похожа на передачу указателя в C ++ с помощью & (например, A * &). Классы не идут в стек. Да, NonNullable позволит иметь ссылку на класс, который гарантированно будет ненулевым, но это совершенно отдельно от ссылки. То, что вы пытаетесь сделать в коде C ++, не работает в D, потому что классы не идут в стек. Структуры делают.
Джонатан М Дэвис
1
Так что да, было бы неплохо иметь ссылку на класс, которая не является nullabe, но C ++ удается делать то, что вы показываете, потому что он позволяет классам находиться в стеке и позволяет разыменовывать указатели. И хотя вы можете разыменовывать указатели в D, классы являются ссылками, а не указателями, поэтому вы не можете разыменовывать их. Поскольку вы не можете поместить их в стек и не можете разыменовать их, в D нет способа встроить класс, который не может быть нулевым. Это является потерей, но NonNullable исправит его, и выгоды от разделения классов и структур , как правило , больше в любом случае.
Джонатан М Дэвис
2
C ++ ссылки не могут быть нулевыми по языковому стандарту (говорить «вероятно, не будет нулевым» неверно, поскольку не может быть нулевым). Я бы хотел, чтобы был способ запретить null как допустимое значение для класса.
Йтернберг
1

Самое важное, что C ++ «делает лучше», чем D, - это взаимодействие с унаследованными библиотеками . Различные 3D движки, OpenCL и тому подобное. Поскольку D новый, он имеет гораздо меньшее количество различных библиотек на выбор.

Другое важное различие между C ++ и D состоит в том, что у C ++ есть несколько финансово независимых поставщиков, но по состоянию на 2014 год у D был практически только один , команда из 2 человек, которая его создала. Интересно, что «принцип второго источника», который гласит, что проекты никогда не должны зависеть от технологий, компонентов, у которых есть только один, единственный поставщик, кажется, справедлив даже для программного обеспечения.

Для сравнения, первая версия интерпретатора Ruby была написана изобретателем Ruby, Юкихиро Мацумото, но основной интерпретатор Ruby эпохи 2014 года был написан практически с нуля другими людьми. Таким образом, Ruby можно рассматривать как язык, имеющий более одного независимого в финансовом отношении поставщика. D, с другой стороны, может быть отличной технологией, но это зависит от нескольких разработчиков, которые ее разрабатывают.

История Java показывает, что даже если технология, в данном случае Java, обладает хорошим, но единым финансистом, существует большой риск того, что технология по существу будет удалена, независимо от огромной корпоративной пользовательской базы. Цитата Apache Software Foundation , где EC означает «Исполнительный комитет»:

Oracle предоставил ЕС запрос на спецификацию Java SE 7 и лицензию, которые противоречат друг другу, строго ограничивают распространение независимых реализаций спецификации и, самое главное, запрещают распространение независимых реализаций спецификации с открытым исходным кодом.

В качестве исторического примечания можно сказать, что апплеты Java имели аппаратное ускорение 3D-холста за годы до разработки HTML5 WebGL. Проблема скорости запуска Java-апплетов могла бы быть решена, если бы Java была проектом сообщества, но руководители единственного финансиста Java, Sun Microsystems, не нашли это достаточно важным, чтобы исправить реализацию Java. Конечный результат: холст HTML5 от нескольких поставщиков в качестве «реплики бедняка» каркасов графического интерфейса Java (Swing и т. Д.). Интересно, что на стороне сервера язык программирования Python обладает теми же преимуществами, которые обещала Java: писать один раз, запускать на каждом сервере, независимо от оборудования, при условии, что приложение Python не скомпилировано в машинный код. Python примерно такой же старый / молодой, как Java, но в отличие от Java, он '

Резюме:

При оценке технологии для производственного использования наиболее важными свойствами технологий являются люди, которые ее разрабатывают, социальный процесс, в котором происходит разработка, и количество финансово независимых групп разработчиков.

Будет ли выгоднее использовать технологию T1 по сравнению с технологией T2, зависит от поставщиков технологий и позволяет ли технология T1 решать связанные с проектом проблемы дешевле, чем T2. Например, если проблема с одним поставщиком была проигнорирована, то для информационных систем Java была бы «лучшей» технологией, чем C ++, поскольку двоичные файлы Java не нуждаются в перекомпиляции при развертывании на новом оборудовании и объеме работ по разработке программного обеспечения, связанных с управлением памятью. меньше для Java, чем для C ++. Проекты, разработанные с нуля, например проекты, которые не зависят от других библиотек, могут быть дешевле в разработке на D, чем C ++, но, с другой стороны, C ++ имеет более одного поставщика и, следовательно, менее рискован в долгосрочной перспективе , (Пример Java, где Sun Microsystems была почти в порядке,

Возможный обход некоторых ограничений C ++

Одним из возможных обходных путей к некоторым ограничениям C ++ является использование шаблона проектирования, в котором исходное задание разбивается на части, а куски «сортируются» (классифицируются, группируются, разделяются, другие-славные слова для то же самое) к 2 классам: управляйте задачами , связанными с логикой , где шаблоны доступа к памяти не допускают локальность; задачи , подобные обработке сигналов , где легко достигается локальность. Задачи обработки сигналов «как» могут быть реализованы в виде набора относительно упрощенных консольных программ. Все задачи, связанные с логикой управления, помещаются в один скрипт, написанный на Ruby, Python или иным образом, где удобство разработки программного обеспечения имеет более высокий приоритет, чем скорость.

Чтобы избежать дорогостоящей инициализации процессов операционной системы и копирования данных между процессами операционной системы, набор небольших консольных приложений C ++ может быть реализован в виде одной программы C ++, которая загружается как «сервлет» в Ruby / Python / etc. скрипт. Рубин / Питон / и т.д. скрипт завершает работу сервлета перед выходом. Связь между "сервлетом" и Ruby / Python / и т.д. Сценарий происходит через именованный канал Linux или какой-либо подобный механизм.

Если исходная задача не может быть легко разделена на части, которые могут быть классифицированы по двум вышеупомянутым классам, то можно попытаться перефразировать проблему так, чтобы первоначальная задача изменилась.

Мартин Вахи
источник