Що C ++ робить краще, ніж D?


135

Я нещодавно вивчаю D і починаю ознайомлюватися з мовою. Я знаю, що це пропонує, я ще не знаю, як все використовувати, і я мало знаю про D ідіоми тощо, але я вчуся.

Мені подобається D. Це приємна мова, яка, певним чином, є величезним оновленням до C, і зроблено чудово. Жодна з особливостей не здається «зафіксованою», але насправді досить продумана і добре продумана.

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

Сам, хоча я знаю С, не можу сказати, що знаю С ++. Мені хотілося б почути від когось, що знає як C ++, так і D, якщо вони думають, що є щось, що C ++ робить краще, ніж D як мова (мається на увазі не звичайне, "у ньому більше сторонніх бібліотек" або "є більше ресурсів" або " існує більше завдань, для яких потрібна C ++, ніж D ").

D був розроблений дуже кваліфікованими програмістами на C ++ ( Вальтер Брайт та Андрій Олександреску за допомогою спільноти D), щоб виправити багато питань, які мали C ++, але чи було щось, що насправді не покращилося? Щось він пропустив? Щось, на вашу думку, не було кращим рішенням?

Також зауважте, що я кажу про D 2.0 , а не D 1.0 .


15
Я переконався, що спільнота D бачить це, оскільки я впевнений, що тут набагато більше C ++, ніж D чортів Так ви отримаєте цікавіші або принаймні різноманітні відповіді.
Клаїм

7
Крім того, D2 був розроблений Вальтером Брайт, але також з Олександреску. Ви можете виправити це у своєму запитанні.
Клайм

2
@Klaim: у D та стандартній бібліотеці також було багато громадських участей.
Міхал Мініч

28
@Anto мовою C ++ набагато краще, ніж D, тому що ви, програміст, ненавидите своє життя.
Арлен

6
@jokoon: Насправді, так, з дуже малою роботою: digitalmars.com/d/2.0/interfaceToC.html
Анто

Відповіді:


124

Більшість речей, які C ++ «робить» краще, ніж D, - це мета речі: C ++ має кращі компілятори, кращі інструменти, більш зрілі бібліотеки, більше прив’язок, більше експертів, більше навчальних посібників тощо. В основному в ньому є більше та краще всіх зовнішніх речей, які можна було б очікувати від більш зрілої мови. Це непорушно.

Що стосується самої мови, то, на мою думку, є кілька речей, які C ++ робить краще, ніж D. Напевно, є і більше, але ось декілька, які я можу перерахувати у верхній частині голови:

C ++ має кращу продуману систему типів
На даний момент існує досить багато проблем із системою типу в D, які, як видається, не помітні в дизайні. Наприклад, наразі неможливо скопіювати структуру const в структуру non-const, якщо структура містить посилання на об'єкти класів або покажчики через транзитивність const і спосіб, як конструктори постбліту працюють на типи значень. Андрій каже, що знає, як це вирішити, але деталей не дав. Проблему, безумовно, можна виправити (введення конструкторів копій у стилі C ++ було б одним виправленням), але це основна проблема мови.

Ще одна проблема, яка мене помилила, - це відсутність логічного const (тобто немає, mutableяк у C ++). Це відмінно підходить для написання безпечного потокового коду, але ускладнює (неможливо?) Проробляти ледачу ініціалізацію в рамках const об'єктів (подумайте про функцію const 'get', яка будує та кешує повернене значення під час першого дзвінка).

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

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

Редагування: Для уточнення, я вважаю, що C ++ має більш продуману систему типів - не обов'язково кращу - якщо це має сенс. По суті, в DI вважають, що існує ризик використання всіх аспектів системи його типу, яка відсутня в C ++.

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

Я не думаю, що це особисто велика проблема, але деякі можуть вважати це недоліком.

D вимагає вивезення сміття
Це може вважатися суперечливим, оскільки можна запустити D без ГК. Однак те, що це можливо, не означає, що це практично. Без 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 vs C ++, тому що це питання задав, але, будь ласка, не сприймайте цю публікацію як твердження, що C ++ кращий за D. Я міг би легко створити більшу кількість місць, де D краще ніж C ++. Ви самі вирішуєте, який саме використовувати.


Я дивився на D кілька років тому (до 2.0). Збирання сміття тоді насправді не було потрібно - воно було за замовчуванням, але ви можете відмовитися від низького рівня коду. Те, що я вважав невірним у цьому, - це те, що я не можу знайти спосіб повернутися назад. Наприклад, у контейнері на основі дерева код бібліотеки може керувати пам'яттю для самих вузлів дерева. Дерево в цілому все ще може бути колекційним, IIRC - деструктор, який збирає всі ці вузли. Але об'єкти, на які посилаються дані в цьому контейнері, також мають бути колекціонованими - у галці має бути гачок для позначення всіх елементів даних у дереві.
Steve314

3
Ви все ще можете відключити GC для коду низького рівня - Петро говорить, що мова від цього зараз багато залежить. Крім того, ви можете сказати GC сканувати діапазони за межами керованої купи за допомогою свого API: GC.addRange з core.memory .
Володимир Пантелеев

+1 для вказівки на те, що стандартна бібліотека D збирається сміттям і що GC-off код не є суцільним інтеропом. Я не про це думав, але це здається головною перешкодою для подолання.
masonk

132

Коли я приєднався до розвитку D, я опинився в особливому положенні бути одним із людей, які там знають більшість - це знати про C ++. Зараз я перебуваю в ще більш своєрідному становищі, щоб бути також одним із людей, які знають більшість, про те, що потрібно знати про D. Я не кажу про це на належні права на кредит або вихваляння настільки, щоб зауважити, що я з цікавістю вигідне становище для відповіді на це запитання. Це ж стосується і Вальтера.

За великим рахунком, запитання про те, що C ++ (і маю на увазі, що C ++ 2011) робить краще, ніж D, настільки ж суперечливе, як і питання: "Якщо ви заплатите професіоналу, щоб прибирати свій будинок, які місця вони залишать? брудніше, ніж раніше? " Яка б цінність не була в тому, що C ++ міг зробити це D, не міг, він завжди стояв, як болячий палець мені і Вальтеру, тому майже за визначенням нічого С ++ ніколи не може зробити, що не досягає D.

Одне з питань, що рідко розуміється в мовному дизайні (адже мало кому пощастило насправді зробити щось) - це набагато менше несподіваних помилок, ніж це може здатися. Багато хто з нас, хто користується мовою, дивляться на якусь конструкцію чи іншу і говорять: "О! Це так неправильно! Що думали?" Справа в тому, що більшість незграбних випадків у мові є наслідком кількох фундаментальних рішень, які є здоровими та бажаними, але принципово конкурують або суперечать один одному (наприклад, модульність та ефективність, лайливість та контроль тощо).

Маючи на увазі все це, я перелічу кілька речей, які я можу придумати, і для кожної я поясню, як вибір D випливає з бажання виконати якусь іншу, вищу версію статуту.

  1. D передбачає, що всі об'єкти переміщуються шляхом розрядного копіювання. Це залишає меншість конструкцій для C ++, зокрема тих, що використовують внутрішні покажчики, тобто клас, що містить вказівники всередині себе. (Будь-яка така конструкція може бути переведена без жодної або незначної вартості на ефективність у D, але для цього потрібні зусилля з перекладу.) Ми прийняли це рішення значно спростити мову, зробити копіювання об'єктів більш ефективним без мінімального або мінімального втручання користувача та уникати весь модний будівельний примірник та посилання на реальну оцінку взагалі.

  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, що об'єктивногірше, ніж C ++. Більшість проблем із D, де можна об'єктивно сказати, що це гірше, - це або питання якості впровадження (які, як правило, пов’язані з тим, наскільки молодий мова та імплементація, і виправляються із швидкістю запізнення), або вони проблеми з нестачею сторонніх бібліотек (що прийде з часом). Сама мова, як правило, краща, ніж C ++, і випадки, коли C ++ як мова краща, як правило, або відбувається там, де відбувається компроміс, коли C ++ пішов в один бік, а D пішов іншим, або де хтось має суб'єктивні причини, чому вони подумайте, що один кращий за інший. Але кількість відвертих об'єктивних причин, чому C ++ як мова краща, швидше за все, небагато.

Насправді мені доводиться по-справжньому загорнути мозок, щоб придумати причини, чому C ++ як мова кращий за D. Що загалом спадає на думку, це питання компромісів.

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

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

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

  4. Двійки модульна система набагато чистіше , ніж директив #includes С ++ (не кажучи вже, шлях швидше при компіляції), але йому не вистачає якихось - або простору імен за самих модулів. Отже, якщо ви хочете простір імен в модулі, вам потрібно пройти маршрут Java і використовувати статичні функції на класі або структурі. Це працює, але якщо ви дійсно хочете простору імен, це, очевидно, не так чисто, як справжній простір імен. Однак у більшості ситуацій простору імен, які надають самі модулі, достатньо (і досить складно, якщо мова йде про речі, як насправді конфлікти).

  5. Як і Java, і C #, D має одинакове успадкування, а не багаторазове успадкування, але на відміну від Java і C #, це дає вам фантастичні способи отримати той самий ефект без усіх проблем, які мають багаторазове успадкування C ++ (і багатократне успадкування C ++ може стати дуже безладним часом). Мало того, що D має інтерфейси , але у нього є рядки Домішки , Mixins шаблон і псевдонім це . Отже, кінцевий результат, мабуть, більш потужний і не має всіх питань, які має багатократне успадкування C ++.

  6. Подібно до C #, D розділяє структури та класи . Класи - це еталонні типи, які мають успадкування і походять від них Object, тоді як структури - цінні типи без успадкування. Це розділення може бути як добрим, так і поганим. Це позбавляється від класичної проблеми нарізки в C ++, і це допомагає окремим типам, які є дійсно цінні типи, від тих, які повинні бути поліморфними, але спочатку, принаймні, це може роздратувати програміста C ++. Зрештою, це має низку переваг, але це змушує вас поводитися зі своїми типами дещо інакше.

  7. Члени функції з класів поліморфні за замовчуванням. Ви не можете оголосити їх невіртуальними . Компілятор повинен вирішити, чи вони можуть бути (що насправді має місце лише в тому випадку, якщо вони остаточні і не перекривають функцію базового класу). Отже, це може бути проблемою продуктивності в деяких випадках. Однак, якщо вам справді не потрібен поліморфізм, то все, що вам потрібно зробити, - це використовувати структури , і це не проблема.

  8. D має вбудований сміттєзбірник . Багато хто з C ++ вважають, що це є серйозним недоліком, і правда, на сьогоднішній день його реалізація може використовувати серйозну роботу. Він вдосконалюється, але це, безумовно, ще не збігається зі збирачем сміття Java. Однак це пом'якшується двома факторами. По-перше, якщо ви в основному використовуєте структури та інші типи даних на стеці, то це не велика проблема. Якщо у вашій програмі не буде постійно розподіляти та розбирати речі на купі, це буде добре. І два, ви можете пропустити сміттєзбірник, якщо хочете, і просто скористаєтеся C mallocі free. Є деякі особливості мови (наприклад, нарізка масивів), чого вам доведеться уникати або бути обережним, а деякі стандартні бібліотеки насправді не можна використовувати без принаймні деякого використання GC (особливо обробляння рядків), але ви можете писати в D, не використовуючи збирач сміття, якщо ти дуже хочеш. Розумне, що потрібно зробити, це, мабуть, загалом використовувати його, а потім уникати його там, де профілювання показує, що це спричиняє проблеми для критичного коду продуктивності, але ви можете його повністю уникнути, якщо захочете. А якість впровадження GC з часом покращиться, усуваючи багато проблем, які може викликати використання 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 ++ є одним із головних співробітників D. Я вважаю, що шаблонів C ++ серйозно не вистачає в порівнянні з D шаблонами, і це може бути дуже неприємно часом при програмуванні на C ++.

  5. D має вбудоване контрактне програмування .

  6. D має вбудований блок тестових рамок.

  7. D має вбудовану підтримку unicode з string(UTF-8), wstring(UTF-16) та dstring(UTF-32). Це полегшує роботу з unicode. І якщо ви хочете просто використовувати stringі взагалі не турбуватися про unicode, ви можете - хоча деяке розуміння основ юнікоду допомагає у виконанні деяких стандартних функцій бібліотеки.

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

  9. Масиви D набагато краще, ніж масиви C ++. Вони не тільки належного типу з довжиною, але можуть бути додані та змінені. З’єднати їх легко. І найкраще, щоб вони мали нарізки . І це величезна користь для ефективної обробки масиву. Рядки - це масиви символів у D, і це не проблема (насправді це чудово!), Оскільки масиви D настільки потужні.

Я міг би продовжувати і продовжувати. Багато вдосконалень, які надає D, - це дрібниці (наприклад, використання thisдля імен конструктора чи заборона, якщо висловлювання або цикли тіл, де крапка з комою є їх усім тілом), але деякі з них досить великі, і коли ви додаєте все це разом, це робить для набагато кращого досвіду програмування. C ++ 0x додає деякі функції, у яких D, яких C ++ бракувало (наприклад, autoлямбда), але навіть з усіма його вдосконаленнями, все ще не так багато, що об'єктивно краще щодо мови C ++, ніж D.

Немає сумнівів у тому, що існує безліч суб'єктивних причин, як сподобатися одне за іншим, і відносна незрілість впровадження D може бути проблемою часом (хоча вона з часом пізніше покращується - тим більше, що сховища були переміщені до github ) , і відсутність сторонніх бібліотек, безумовно, може бути проблемою (хоча факт, що D може легко викликати функції C - і меншою мірою, функції C ++ - безумовно, пом'якшує проблему). Але це питання якості впровадження, а не проблеми з самою мовою. І як питання щодо якості впровадження будуть виправлені, використовувати його стане набагато приємніше.

Отже, я гадаю, що коротка відповідь на це запитання "дуже мала". D, як мова, як правило, перевершує C ++.


2
Мови збору сміття використовують на 2-5 разів більше пам’яті, ніж не з GC (за повідомленнями Олександреску на YT), так що, безумовно, проблема, якщо це (використання пам’яті) є вузьким місцем.
NoSenseEtAl

9

RAII та використання пам'яті стека

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

Ви не можете виконати успадкування типу значення в D, настільки ефективно, що змушує вас робити розподіл купи для будь-якої форми RAII.
Тобто, якщо ви не користуєтесь emplace, але це дуже болісно використовувати, оскільки вам потрібно виділити пам'ять вручну. (Я ще не можу знайти практичне застосування emplaceу D.)


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тощо.
Для кращого чи гіршого значення С ++ є більш багатослівним.

Звичайно, ви могли також зробити це в 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));
        };
    }
}

і вони виглядали б майже однаково, але тоді це вимагало б delegate, тоді як оригінал приймав будь-який об'єкт, що дзвонить. (Версія C ++ 0x вимагаєstd::function об'єкта, тому в будь-якому випадку вона є більш багатослівною і обмежувальною у своїх входах ... що може бути добре, якщо вам подобається багатослівність, погано, якщо ви цього не зробите.)


2

Я мало знаю про D, але багатьох, багатьох програмістів на C ++ мені це дуже не подобається, і мені особисто доводиться погоджуватися - мені не подобається погляд D і не буду брати його ближче.

Щоб зрозуміти, чому D не набирає більшої тяги, для початку потрібно зрозуміти, що приваблює людей до C ++. Одним словом, причина номер один - це контроль. Коли ви програмуєте на C ++, то ви маєте повний контроль над своєю програмою. Хочете замінити бібліотеку Standard? Ти можеш. Хочете зробити небезпечні ролики вказівників? Ти можеш. Хочете порушити коректність? Ти можеш. Хочете замінити розподільник пам'яті? Ти можеш. Хочете скопіювати навколо необробленої пам'яті, не враховуючи її тип? Якщо ти дуже хочеш. Хочете успадкувати від кількох реалізацій? Це ваше похорон. Чорт, ви навіть можете отримати бібліотеки для збору сміття, як колектор 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-бітове. Це також зовсім не розширюється - уявіть, що буде, якби кожен винайшов власні специфікатори формату. Іострими C ++ можуть бути повільними, і їх вибір оператора може бути не найбільшим, і їхній інтерфейс може використовувати роботу, але вони принципово гарантовані як безпечні, і DRY не порушуються, і їх можна легко розширити. Це не те, про що можна говорити printf.

Немає багаторазового успадкування. Це зовсім не спосіб С ++. Програмісти на 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 потрібно зробити кращу роботу з рекламою.


9
@DeadMG: Це на 100% невірно, і немає сенсу сказати. Це, безумовно, пониження рівня від C ++, що просто є, std::functionі ви закінчили. Чому? Тому що ви, наприклад, маєте функціональні покажчики. Це абсолютно те ж саме в D: "функції" D - це покажчики функцій, а "делегати" D - такі ж, як і C ++ std::function(за винятком того, що вони вбудовані). Ніде нічого не знижують - а між ними листування 1: 1, тож це не повинно бентежити, якщо ти знайомий із C ++.
Мехрдад

10
@Mark Trapp: Треба визнати, що я не дуже розумію вашу позицію щодо теми - коментарі не слід використовувати, якщо ви знаєте, коментуючи відповідь?
klickverbot

6
@Mark Trapp: Моя думка полягає в тому, що більшість коментарів тут не застаріли (мета-дискусія, яку ви пов’язали спеціально, стосується пропозицій, які вже були включені в оригінальний пост), але вказала на фактичні неточності в публікації, які все ще присутні .
klickverbot

7
Примітка щодо формату: Функція формату D є typesafe (вирішує проблеми безпеки / переповнення) і не порушує DRY, оскільки рядок форматування визначає лише те, як слід форматувати аргументи, а не їх типи. Це можливо завдяки безпечним варіантам D. Тому це заперечення принаймні є абсолютно недійсним.
Justin W

17
@Mark: Яка б не була поточна політика щодо них, я вважаю це дурним і заважаю дискусіям щодо коментарів видалити . Я думаю, що ця відповідь мала широкі дискусії (що мене зараз цікавить), але я не впевнений, і я не можу це дізнатися. У цій кімнаті, з якою ви зв’язалися, є понад 10 тис. Повідомлень, і я не маю шансів знайти дискусію, яку, здається, пам’ятаю, що відбулася, але не можу запам'ятати вміст. Дискусії щодо цієї відповіді належать тут, до цієї відповіді , а не до якоїсь чатової кімнати, де вони могли б змішатися в дискусіях про секс, наркотики та рок-н-ролл.
sbi

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 ++, зробивши AD structі позначивши foo()-аргумент як ref(класи - це еталонні типи, а структури - це значення типу D, подібні до C #).

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


3
Як аргументи функції, так і значення, що повертаються в D, можуть бути позначені символом, refщоб отримати такий же ефект, як і C ++ &. Основна відмінність полягає в тому, що refце не буде тимчасовим, навіть якщо воно є const.
Джонатан М Девіс

Мені подобається те, як заборонено повернення посилань на локальні змінні в D, про це я не знав, поки не прочитав ваш коментар. Але ви все одно можете повернути не локальну нульову посилання, не замислюючись про це так, коли оператор скидання C змусить вас задуматися.
люмор

Ви плутаєте речі. Класи - це завжди посилання, і це окремо від посилання. Посилання в D схожі на посилання на Java. Їм керують покажчики. Проїзд або повернення за посиланням - це як проходження або повернення з & в C ++. Передача посилання на клас за посиланням - це як передача вказівника на C ++ за допомогою & (наприклад, A * &). Заняття не проходять на стеці. Так, NonNullable дасть можливість мати посилання на клас, який гарантовано був ненульовим, але це повністю окремо від посилання. Те, що ви намагаєтеся зробити в коді C ++, не працює в D, оскільки класи не переходять на стек. Структури роблять.
Джонатан М Девіс

1
Так, так, мати змогу мати посилання на клас, який не є nullabe, було б непогано, але C ++ вдається зробити те, що ви показуєте, тому що це дозволяє класам знаходитись на стеці та дозволяє перенаправлення покажчиків. І хоча ви можете перенаправити покажчики в D, класи - це посилання, а не покажчики, тож ви не можете їх знецінювати. Оскільки ви не можете помістити їх у стек і не можете їх знеструмити, у D немає жодного способу мати клас, який не може бути нульовим. Це є втратою, але NonNullable виправить його, і вигоди від поділу класів і структур , як правило , більше в будь-якому випадку.
Джонатан М Девіс

2
Посилання на C ++ не можуть бути нульовими за мовним стандартом (вислів "напевно не буде нульовим" є невірним, оскільки він не може бути нульовим). Я хочу, щоб був спосіб заборонити null як дійсне значення для класу.
jsternberg

1

Найголовніше, що C ++ "робить краще", ніж D, - це взаємодія зі застарілими бібліотеками . Різні 3D двигуни, OpenCL тощо. Оскільки D є новим, він має набагато меншу кількість різних бібліотек на вибір.

Ще одна важлива відмінність між C ++ і D полягає в тому, що C ++ має декількох фінансово незалежних постачальників, але станом на 2014 рік у D є практично лише один , команда з двох чоловік, яка його створила. Цікаво, що "принцип другого джерела", який говорить про те, що проекти ніколи не повинні залежати від технології, компонентів, які мають лише одного, єдиного постачальника, схоже, навіть для програмного забезпечення.

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

Історія Java показує, що навіть якщо технологія, в даному випадку, Java, має прекрасного, але єдиного фінансиста, існує великий ризик, що технологія по суті буде скинута, незалежно від величезної бази корпоративних користувачів. Посилання на програмний фонд Apache Software , де ЄК виступає за "Виконавчий комітет":

Oracle надав EC запит та ліцензію 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, де Мікросистеми Сонця було майже нормально,

Можливе вирішення деяких обмежень C ++

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

Щоб уникнути дорогої ініціалізації процесів операційної системи та копіювання даних між процесами операційної системи, набір невеликих консольних додатків C ++ може бути реалізований у вигляді однієї програми C ++, завантаженої Ruby / Python / тощо. сценарій. Ruby / Python / тощо. сценарій вимикає сервлет перед виходом. Зв'язок між "сервлетом" та Ruby / Python / тощо. скрипт відбувається через Linux з назвою pipe або якийсь подібний механізм.

Якщо початкове завдання не може бути легко розділене на частини, які можна класифікувати до двох вищезгаданих класів, тоді спробувати можна переформулювати проблему, щоб початкове завдання змінилося.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.