Почему Ваза утонул, а С++ всё ещё на плаву

  • Tutorial
Эта статья — краткий пересказ невероятно интересного доклада Скотта Майерса для тех, у кого нет 70 минут на весь доклад, но есть 7 минут на основные тезисы.

Некоторые люди, которые не пишут на С++, а лишь слышали об этом языке, задаются вопросом: «Почему вообще кто-то пишет на C++?». Но есть люди, которые используют С++ каждый день, и вот эти люди задаются вопросом: «А действительно, почему я пишу на этом языке?».

Но ведь действительно, должна быть какая-то причина, по которой люди пишут программы на С++. Давайте вернемся в начало 90-ых, когда проходила стандартизация С++. Была предложена масса идей. Предложений было столько и они были настолько разные, что мне запомнилась цитата Джима Вальдо, который тогда работал в комитете по стандартизации: «Каждый, предлагающий добавить что-то в С++ должен приложить к заявке свою почку. Тогда никто не предложит больше двух идей, а к выбору этих двух он подойдёт невероятно ответственно.»

Язык, который был бы получен в результате принятия всех предложений, выходил слишком сложным и тогда Бьёрн Страуструп сказал «А помните Ваза?». Никто, кроме людей из Швеции, не понял о чём речь. Ваза был огромным боевым кораблём, построенным в Швеции в 1625 году. Основным принципом постройки корабля было «А почему бы нам не добавить сюда ещё и вот такую фичу?». Многие из идей исходили непосредственно от короля, в частности он лично утверждал размеры корабля. Также на Ваза по указаниям свыше требовалось нацепить огромное количество элементов украшения, резьбы, большое количество пушек и т.д. А королю ведь не откажешь. Итог был закономерным — из-за ошибок в конструировании Ваза затонул в первом же рейсе, едва выйдя из бухты.

Давайте посмотрим на С++. Является ли он настолько же «нагруженным» фичами? На первый взгляд — без сомнения:



Давайте посмотрим вот на это:
f(x);


Вроде бы всё ясно — это функция f, которой передаётся х. Так? Не факт. Это может быть функция, а может быть указатель на функцию, а может быть ссылка на функцию. Ещё это может быть объект, в классе которого перегружен оператор скобок. Или объект, который неявно может быть сконвертирован в что-то из вышеуказанного. Или одна из перегруженных функций. Или шаблон. Или несколько из вышеуказанных вещей одновременно.

Я начинаю понимать, почему люди считают С++ сложным. Кажется, что всё вышеуказанное спроектировано совершенно неверно. Слишком сложно.
Но подождите. Давайте измерим пару вещей. Например, количество вакансий для программистов на разных языках, количество кода на них в открытых источниках, количество поисковых запросов к поисковикам и т.д. И если мы посмотрим на статистику, в течении последних 25 лет С++ находится на 2-4 месте по популярности в мире.



С++ на втором месте по количеству кода в opensource (12%) и этот процент растёт год от года.



Соответственно, что-то в этом языке сделано правильно. Почему-то он плывёт. Давайте разбираться почему.

Совместимость с С

Мы можем взять код на С — и использовать его в С++. Мы можем взять редактор, IDE, линкер, дебаггер от С — и использовать их для С++ сразу или с минимальными изменениями. Мы можем взять программиста на С, сказать ему «ты уже знаешь часть фич С++, посмотри на ООП, STL и ещё пару вещей — и ты сможешь писать на С++». Эта вещь была невероятно важной в 90-ых и начале 2000-ых. Да и сегодня код на С — самый распространённый в мире.

Самая важная фича языка

Что бы вы назвали самой важной фичей языка? (Реплики из зала: «фигурные скобки», «шаблоны»). Нет. Это деструкторы. Деструкторы сделали возможным RAII. Деструкторы упростили сложность используемых в программировании концепций на порядок. В деструкторе мы можем освободить память, мьютекс, закрыть файл или сетевое соединение. Деструкторы спасают нас от веток условий, от ненужных goto.

Шаблоны

На Марсе есть небольшой такой кратер, который получился от того, что в эту планету бодро врезался сделанный на Земле исследовательский аппарат, который вообще-то разбиваться был не должен. Но разбился. Разбился потому, что в одном из кусков его кода программист решил использовать метрическую систему мер, а в другом куске кода, другой программист — классическую английскую. И вот как-то оказалось, что килограмм не равен фунту.
Шаблоны в С++ дали нам возможность писать абстрактный код, алгоритмы, функции и классы, которые на этапе компиляции могут надёжно и однозначно быть специфицированы какими-то определёнными типами. Это огромный шаг вперёд. И никаких затрат на рантайме!

Перегрузка

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

Язык для библиотек

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

Мультипарадигменность

Процедурное программирование? Ок. Объектно-ориентированное программирование? Ок. Метапрограммирование на шаблонах? Ок. «Опасное программирование» с указателями и ассемблерными вставками? Ок. Смешанные концепции? Тоже ок. Даже функциональное программирование в какой-то мере уже становится возможным с приходом последних стандартов С++.

Лучший язык для «программирования с препятствиями»

Возможно, С++ не лучший язык всех времён и народов, но на данный момент он лучший язык для того, что я называю «программированием с препятствиями». Препятствием может быть неизвестная предметная область, высокие требования по быстродействию или потребляемой памяти, необходимость общаться со странным чужим кодом или аппаратными устройствами, неопределённость задач и т.д. Выбрав С++ в ситуации с наличием вышеуказанных (или других) препятствий — у вас самые высокие шансы не запороть проект в какой-то момент просто потому, что на выбранном языке решить задачу невозможно.

Вы не платите за то, что не используете

Один из базовых принципов языка. То, что может быть просчитано на этапе компиляции — там и просчитывается. Неиспользуемый код — не вызывается. Программа отрабатывает только те инструкции, которые вы написали, именно в том порядке, в каком вы заставили её это делать. Никаких закулисных игр. Такие вещи особенно важны в системном программировании.

«Платформенно-независимый ассемблер»

Еще одно свойство совместимости с С. Программа на С++ не так далеко ушла от ассемблера. В большинстве случаев можно понять в какой набор ассемблерных инструкций развернётся код на С++. Как результат — можно писать эффективные программы под любые платформы. Более того, под некоторые платформы только на С/С++ писать и можно.

Обратная совместимость

Код на С++, написанный в начале 90-ых на первых версиях стандарта С++ можно просто взять и скомпилировать современным компилятором, поддерживающим С++14. И он скомпилируется. Ну, с большой вероятностью. И будет делать то, что должен. Опять таки, с очень большой вероятностью. Есть пару мест, которые могут сломаться, но их мало и они общеизвестны. Таким образом мы говорим компании: инвестируя сегодня в код на С++ вы можете быть спокойны — и через 10 и через 20 лет ваш код будет работать, а значит ваши деньги не пропадут.

Сложность, которую можно спрятать

Языку С++ часто пеняют тем, что код на нём — сложный. Вернее было бы сказать, что на С++ действительно можно (при желании) написать сложный код. И ещё о коде на С++ можно сложно говорить. Давайте взглянем на вот это:

std::cout << x;


О Боже мой! Да здесь же есть пространства имён, потоки, шаблон (неявно специфицированный), объект класса (унаследованного от другого класса) и перегрузка операторов. Кошмар! Но взгляните на код выше ещё раз. Вы правда не понимаете, что там написано? Вы правда задумываетесь о всех этих страшных терминах, которые я упоминал, когда читаете этот код? Нет. Сложность прячется от вас, вы её не замечаете.

Язык для тех, у кого нет другого выбора

Люди, которые решают писать на С++, живут не в танке. Зачастую они знают парочку других языков — скриптовых, управляемых, функциональных. Там, где можно сэкономить себе время и силы — они на них и пишут. Но иногда оказывается, что выбора нет — проблема слишком сложна, требования слишком высоки, риски запредельны. И тогда на сцену выходит С++. Сложный, но мощный инструмент для сложных, но масштабных задач. Это, к стати, одна из причин, почему программирование на С++ и код на С++ считаются сложными — средний уровень решаемых на этом языке задач является более высоким. Наивно ждать решения сверх-сложной задачи простым минималистичным кодом.

Язык для тех, кто любит сложности

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

Стандарт становится больше и… проще

Нам говорят, что в стандарте языка С++ в 1990-ом году было 400 страниц, в 1998 — 700, а в 2011 стало 1300. «Язык становится сложнее» — нам говорят. Нет, не становится. Да, мы вводим новые понятия и фичи, но большинство их них призваны не усложнить, а упростить код. Вспомните, как вам приходилось бегать итераторами по векторам — и вот в С++11 у нас есть range-based for и auto. Мы можем написать одну строку кода там, где раньше было 5. Лямбда-функции дали возможность исключить из кода целые сущности — лишние классы и методы, которые по факту были не нужны. Да, разработчикам стандарта приходится обдумывать и писать существенно больше текста. Но конечному программисту становится только легче.
Метки:
Инфопульс Украина 278,49
Creating Value, Delivering Excellence
Поделиться публикацией
Комментарии 261
  • +16
    std::cout << x;
    

    Если взглянуть на эту строчку глазами человека, который вообще ни разу не имел дела с C++, то она совершенно непонятна. Максимум, что можно сказать — что-то делается с переменной x. Или это не переменная? О значении идентификатора cout и сдвига без знания конвенции не догадаешься — хотя согласен, что привыкаешь к ней довольно быстро.
    • +26
      Если некоторые люди взглянут на некоторые строчки некоторых языков, с которыми они никогда не имели дела, то они могут показаться им совершенно непонятными. И это относится к любому языку программирования, всегда найдутся конструкции, в которых без детального изучения не разобраться.
      • +2
        echo explode(":", "foo:*:1023:1000::/home/foo:/bin/sh");
        

        Регэкспы ещё можно вспомнить, вот уж где точно
        без знания конвенции не догадаешься
        \b(?:\d{1,3}\.){3}\d{1,3}\b
        • +5
          Регекспы — отличный пример write-only кода. Но лучше пока никто ничего не придумал (вербальные выражения не в счет).
          • +12
            А по-моему, write-only код — это код людей, которые не зная reg exp'ов фичачат вместо одного паттерна 35 strpos.
            • +4
              Вспомнилось
              bool IsBukva(char symbol)
              {
              switch(symbol)
              {
              case'a':return 1;break;
              case'b':return 1;break;
              case'c':return 1;break;
              case'd':return 1;break;
              case'e':return 1;break;
              case'f':return 1;break;
              case'g':return 1;break;
              case'h':return 1;break;
              case'i':return 1;break;
              case'j':return 1;break;
              case'k':return 1;break;
              case'l':return 1;break;
              case'm':return 1;break;
              case'n':return 1;break;
              case'o':return 1;break;
              case'p':return 1;break;
              case'q':return 1;break;
              case'r':return 1;break;
              case's':return 1;break;
              case't':return 1;break;
              case'u':return 1;break;
              case'v':return 1;break;
              case'w':return 1;break;
              case'x':return 1;break;
              case'y':return 1;break;
              case'z':return 1;break;
              case'A':return 1;break;
              case'B':return 1;break;
              case'C':return 1;break;
              case'D':return 1;break;
              case'E':return 1;break;
              case'F':return 1;break;
              case'G':return 1;break;
              case'H':return 1;break;
              case'I':return 1;break;
              case'J':return 1;break;
              case'K':return 1;break;
              case'L':return 1;break;
              case'M':return 1;break;
              case'N':return 1;break;
              case'O':return 1;break;
              case'P':return 1;break;
              case'Q':return 1;break;
              case'R':return 1;break;
              case'S':return 1;break;
              case'T':return 1;break;
              case'U':return 1;break;
              case'V':return 1;break;
              case'W':return 1;break;
              case'X':return 1;break;
              case'Y':return 1;break;
              case'Z':return 1;break;
              default:return 0;
              }
              }
              
              • +2
                Дададада! Я как-то давно (ОЧЕНЬ ДАВНО) читал код чудо-движка DLE, тогда он был открыт и бесплатен. Так вот, там 50% (очевидно старого кода) было написано с тыщам strpos, а потом автор, очевидно, выучил рег экспы и в более новом коде появились кривые паттерны в стиле \S*, но это уже всяко был прогресс против strpos-то =)
                • +2
                  Стоит отметить, что в PHP иной раз замена через regexp может быть в 30 раз медленнее замены через str_replace, поэтому шутки шутками, но без бенчмарков судить сложно.
                  • +2
                    Ну если посмотреть на то, как сделаны regexp'ы в PHP, то можно придти к выводу, что язык явно писали садисты. Ибо работа с regexp'ами состоит из двух частей:
                    1) Компиляции regexp'ов (очень медленно и требует кучу ресурсов)
                    2) Использование regexp'ов (быстро и просто)
                    Что делают нормальные люди? Разделяют эти стадии явно (C/C++, Java, etc), либо неявно (JavaScript, Perl, etc). Что делают разработчики PHP? Объединяют их на уровне API и гарантируют, что это это мощное, быстрое и лёгкое средство будет жрать ресурсы как не в себя.
                    • 0
                      Не могу вспомнить, чтобы в результатах профилирования на реальных проектах у меня preg_* функции показывали какой-либо заметный процент.

                      Вот сейчас как раз открыт результат профилирования. preg_match вызывается 6494 раз. Суммарное время выполнения — 14 мс (это на 2 секунды работы скрипта). preg_replace вызывается 828 раз. 1.7 мс.

                      Для сравнения по встроенным функциям:
                      Функция — число вызовов — суммарное время, мс
                      mysql_query — 54 --123
                      fclose — 4 — 44
                      file_put_contents — 297 — 26
                      file_get_contents — 55 — 16

                      Вы точно не путаете с древними ereg_* — вот те были реально тормозными.



                      Нашёл в логах пример потяжелее, там шла конвертация аж на 42 секунды длительностью. Там:
                      preg_match — 45883 раз — 28мс
                      preg_replace_callback — 9080 раз — 156мс
                      preg_replace — 658 раз — 1.4 мс
                      • +1
                        Вы путаете «кисло с пресно» — операции с БД и файловые операции всегда очень дорогие. Заведомо дороже регэкспов, которые только в памяти. Есть ли операции не связанные с этими сущностями, но отнимающие сравнимое количество времени?
                        • +1
                          Я к тому, что «преждевременная оптимизация — зло» © Да, существуют задачи, в которых скорость работы со строками критична. Но PHP тут окажется не лучшим выбором хоть с preg-функциями, хоть со str. А на тех задачах, где этот язык востребован, скорость работы preg оказывается совершенно пренебрежимым фактором.
                          • 0
                            Вообще я так подозреваю, что работа с файлами — не основная специализация PHP. Или я отстал от жизни? А если вычесть файловые операции, то доля регулярок достаточно значимо возрастёт. Впрочем без полных данных профилирования сложно что-то говорить. И в любом случае — изначально скорость регулярок сравнивалась со скоростью прямого манипулирования со строками.
                            • 0
                              Да при чём тут файловые операции. Я Вам про время работы регекспов относительно типичного времени работы скрипта. И, если уже про основную специализацию, то Вы очень вольно пропустили mysql_query. Если выкинуть из этого примера файловые операции, доля регулярок вырастет, но всё равно составит пренебрежимую величину.

                              И в любом случае — изначально скорость регулярок сравнивалась со скоростью прямого манипулирования со строками.


                              Это где так было? Я такого не видел ни в одном языке. В том числе в Си (как пример «быстрого языка»), и в Perl'е (как примере языка, для которого регулярки — основа).
                              • 0
                                «Типичного» времени работы скрипта? Что же это за скрипт, который целых две секунды работает?

                                Это где так было? Я такого не видел ни в одном языке. В том числе в Си (как пример «быстрого языка»), и в Perl'е (как примере языка, для которого регулярки — основа).

                                Это было в этой ветке комментариев.
                      • 0
                        Я бы так не сказал. Написав добрых несколько дюжин веб пауков и парсеров, для себя сделал вывод, что где нет простых каких-то замен, то regexp явно в выигрыше. Один регексп лучше десяти str_replace или strpos + substr.
                      • 0
                        … и не только в PHP (те же boost regexp и pcrecpp в n раз медленнее операций со строками).
              • +2
                \b(?:\d{1,3}\.){3}\d{1,3}\b

                Ни на одном языке не пишут в одну строчку, «доказывая» нечитаемость кода, а на регулярках — всегда пожалуйста. Код форматировать надо.

                \b
                (
                    \d{1,3}
                    \.
                ){3}
                \d{1,3}
                \b

                И внезапно регулярка уже читается, причём безо всяких извращений вроде «verbal expressions». Здесь достаточно помнить значение «b» и «d» (множители и скобки забыть сложно).

                Этот режим (ignore whitespace + explicit capture) поддерживается всеми нормальными библиотеками, поэтому, если кто-то продолжает писать write-only код, то он сам себе злобный Буратино.
                • +12
                  помоему стало только хуже.
                  • 0
                    Вы серьёзно? Неужели вы так сломали себе мозг, что код написанный в строку, для вас понятнее, чем табулированный и разобранный? Я бы еще комментарии добавил. Когда мне приходилось в Java работать с регулярными выражениями, я их так и писал. И читать регексп любой сложности — пустяшное дело.
                    • 0
                      Вы их настолько разбиваете? Такие короткие регекспы обычно оставляют одной строкой.
                      • 0
                        Да просто пример неудачный. Когда регекспа больше в 3-4 раза и можно натыкать внутрь комментариев, тогда да, она становится читаемой.
                        • 0
                          Regexp становится читаемым, если смотреть на него в каком-нибудь визуальном тестере, типа вот этого: www.debuggex.com/

                          А так всё это форматирование, комментарии для накрученных регекспов — это как мёртвому припарки (утрировано говоря).
                        • 0
                          Это — вопрос уровня квалификации и понимания читающего. Я в регекспах довольно слаб. Хотя, конечно, думаю, бил бы чуть крупнее. Но я понял заявление в духе «в строчку было бы лучше» и только.
                • +13
                  Взглянем на строчку из английского языка, никогда его не учив. Максимум что понятно, это то, что это надо как-то произнести…
                  • –1
                    Ваша аналогия не совсем корректна. Представьте ситуацию: вы показываете человеку, который не знает английского и далек от программирования, распечатку кода на, прости господи, языке 1C. Даже в таком случае он уловит какую-то часть логики, потому что она выражается через общеупотребительные слова. А вот догадаться без мануала, что делают функции с названиями вроде gcvt или wcstombs не сможет даже опытный программист, свободно говорящий по-английски.
                    • +4
                      Нет, верна, вы выбрали наиболее специфичный синтаксис и требуете от него кристальной понятности, для человека которой ни с плюсами, ни с си, ни вообще с *nix не работал.
                      Конструкции if while, вызова функций ну т.д. в плюсах теже, что и в других языках и общая понимаемость вполне себе.

                      P.S На закуску, не из плюсов: () => {x+=1; y+=1;}
                      • –3
                        Любой шарпист поймет это без всякой помощи
                        • +2
                          Ну да из них, а кроме кто-то поймет?
                          • 0
                            Лямбда?
                            P.S. не шарпист
                            • 0
                              Да, она самая. Наверное стоило написать что-то типа того

                              () => x+=1;
                        • 0
                          (x,y) => {x+=1; y+=1;}
                          • 0
                            Хм, а вот так, на мой взгляд, даже для непосвященного человека будет понятно:
                            Самое сложное в программировании — это называть переменные понятными именами

                            var increaseXandYandPrintResult = new Action<int, int>((x, y) =>
                            {
                            x += 1;
                            y += 1;
                            Console.WriteLine(x);
                            Console.WriteLine(y);
                            });
                            • 0
                              Ну коль пошло в статье и стартовым коменте писать в подобном стиле, то и тут продолжим. А так — выбор имен переменных это наше все.
                            • +1
                              Нет. Это pure лямбда. У DancingOnWater было замыкание.
                              • 0
                                Да, и правда что.
                              • 0
                                А вот и нет :)

                                В первом случае это была функция с замыканием — x и y ловились из контекста.
                                В вашем случае это просто функция от двух переменных, а замыкание отсутствует.
                            • +1
                              ммм… Прочитал всю ветку, никто так и не задал этого вопроса. Простите, а зачем Вы показываете человеку, не знающему английского и далекому от программирования распечатку кода? И какая польза от того, что он уловит какую то часть логики кода, если этот код, прости господи, на языке 1С?
                          • +2
                            Это просто перегрузка оператора <<. Вы можете использовать put, или что еще там в api есть совершенно спокойно. Кстати говоря это еще очень простая особенность синтаксиса, любой программист должен знать, что << — оператор сдвига, и если увидеть вот такой код:
                            std::string name;
                            std::cout << "Enter your name: ";
                            std::cin >> name;
                            std::cout << "Hello, " << name << "!\n";
                            

                            То программист с легкостью поймет, что операторы сдвига каким-то образом позволяют считывать и записывать информацию, не зная о перегрузках, потоках и прочих C++ наворотах
                            • 0
                              А вот это, например, что, вывод, сдвиг, что-то ещё?
                              std::cout << 1 << 1;
                              • +4
                                А в чем проблема с этим кодом? Если хочется сделать сдвиг — поставьте скобки, или запишите результат в переменную.
                                int result = 1 + 2 / 3 * 5 - 11 + 12 / 15; — вот тут например тоже сразу не понятно что будет, надо напрячься и вспомнить про приоритеты операций, так что любые операции с операторами в любом языке можно написать понятнее — было бы желание)
                                • +1
                                  Ну вообще-то дело в моём примере не в приоритетах, а в ассоциативности.
                                  А если хочется фокуса с приоритетами, то вот:
                                  std::cout << a & b;
                                  • +4
                                    Простите, но когда программист пишет, что ему надо напрячься и вспомнить про приоритеты арифметических операторов, я испытываю когнитивный диссонанс. Даже хотя прекрасно знаю о холиворах не тему значения выражения 2+2*2. Всё равно не могу привыкнуть к этому дивному новому миру.
                                    • –1
                                      не очень понимаю, в чем когнитивный диссонанс. Выражение, которое я указал выше, или подобный финт с тернарными операторами заставляет программиста задуматься там, где можно было бы этого не делать.
                                      • +2
                                        Извините, но в Вашем примере я вижу только голую арифметику. Это явно не тот пример, который должен демонстрировать вопросы сложности приоритетов.
                                • –1
                                  > Это просто перегрузка оператора
                                  Скажите, а какая вообще связь между сдвигом и вводом-выводом? Какой логикой нужно было руководствоваться, чтоб использовать операторы сдвига для IO? Не монетку же Страуструп кидал, должно же быть хоть какое-то обоснование использования именно этих операторов, а не, скажем,! или:
                                  • +7
                                    Потому что "!" — восклицание, ассоциируется с отрицанием.":" у меня лично не с чем не ассоциируется. А вот сдвиг, учитывая то, что у нас потоки — вполне. Есть поток мы из него, или в него что-то сдвигаем. В чем проблема-то?
                                    • +1
                                      Проблема и в произвольном выборе оператора, и в не самом низком приоритете выбранного оператора (почему, например не оператор ","), и в костыльном методе задания формата вывода (вот, например, не глядя в документацию, сможете вывести через std::cout аналогично printf("%#10x", a)?).

                                      А вот так не судьба было сделать:

                                      std::cout.out(a).out(format-spec, b);
                                      • –2
                                        Сдвигаем в поток? Выдвигаем из потока? Для вас это имеет какой-то смысл? Для меня нет.
                                        • +4
                                          Объект перенаправить в поток\поток перенаправить в объект. Стандартная операция *nix-ов
                                          • 0
                                            А чего тогда не стандартные обозначения > и <?
                                            • +5
                                              потому что << — добавление в конец потока, например echo blabla >> file.log
                                              • –5
                                                А, а << — это, стало быть, извлечение из конца потока? Или не из конца? Ведь < извлекает из начала. Или не из начала?
                                                А почему у вас поток справа, а в c++ — слева?
                                              • +2
                                                Потомучто это операции сравнения больше и меньше — еще более стандартное обозначение
                                                • –1
                                                  Чтобы можно было один поток с другим сравнить? Или с объектом, чтобы понять, влезает он в поток, или не влезает?
                                                  • +1
                                                    Не понял вопроса
                                                    • –3
                                                      Операторы «больше» и «меньше» — зачем они потоку? Чтобы можно было один поток с другим сравнить? Или с объектом, чтобы понять, влезает он в поток, или не влезает? Я уже конечно совсем стебусь.
                                              • +1
                                                > Стандартная операция *nix-ов
                                                Да, вполне вероятно, что взято оттуда. Об этом не подумал.
                                          • +1
                                            должно же быть хоть какое-то обоснование использования именно этих операторов

                                            Так визуальная составляющая и была основой для выбора. Операторы напоминают стрелку. Собственно для обозначения сдвига они используются ровно по той же причине.
                                        • +1
                                          Иронично — когда я учился в колледже и нам преподавали C++, ввод/вывод с помощью cout/cin считался самым простым и годным.
                                          • +2
                                            Это пока не понадобится какое-никакое форматирование. А там — адов ужас в виде манипуляторов.
                                        • +8
                                          Не читал статью, но сразу хочу сказать — посмотрите доклад — Вы ничего не потеряете — Скотт ооочень интересно рассказывает.
                                          Спасибо за статью
                                          • +4
                                            Без сомнения доклад лучше. Статья — только для экономии времени.
                                          • +24
                                            Программа отрабатывает только те инструкции, которые вы написали, именно в том порядке, в каком вы заставили её это делать. Никаких закулисных игр. Такие вещи особенно важны в системном программировании.

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

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

                                            P.S.: Я понимаю что это пересказ, но в оригинале скорее всего Майерс говорил по другому.
                                            • +4
                                              Я уж точно не вспомню как он говорил, а пересматривать час видео — лень, но в общем главным тезисом было «вы не платите за то, что не используете». Если исключение не возникло — try\catch не стоит ничего. Если вы не вызвали удаление объекта сами — вы можете быть уверены, что никакой там «сборщик мусора» не решит внезапно это сделать за вас в самый неподходящий момент. А то, что в языке есть много случаев неопределенного поведения — ну, это факт. Но это ведь не по глупости или недосмотру, а для того, чтобы дать свободу компилятору генерировать быстрый код.
                                              • +1
                                                Вы не платите за то, что не используете
                                                Я хоть что-то сказал против этого? Я говорю что обозначенные мной утверждения могут ввести в заблуждение. К слову, неопределенное поведение это вообще отдельная тема. Тут речь о синтаксически и семантически корректных ситуациях, которые тем не менее, могут быть оформлены компилятором по своему усмотрению. Так что это ненуточняемое поведение.
                                                • +8
                                                  Ващпе-то компилятор волен не только менять инструкции местами, но и целые блоки кода. К примеру, в gcc:
                                                         -freorder-blocks
                                                             Reorder basic blocks in the compiled function in order to reduce
                                                                 number of taken branches and improve code locality.
                                                             Enabled at levels -O2, -O3.
                                                  

                                                  Если исключение не возникло — try\catch не стоит ничего.

                                                  Это зависит от модели исключений. Модель setjmp/longjmp тормозит программу, даже если исключение не возникло.
                                                  • +1
                                                    Модель setjmp/longjmp — это худшее, что вообще придумано на тему. Зачем её использовать, если уже давно все сделали лучшие альтернативы?
                                                  • 0
                                                    Если вы не вызвали удаление объекта сами — вы можете быть уверены, что никакой там «сборщик мусора» не решит внезапно это сделать за вас в самый неподходящий момент.

                                                    Планировать работу сборщика мусора можно и в managed-языках (и опытные программисты с этим справляются). А вот утечки памяти — это чаще c/c++-ная проблема.
                                                    • +4
                                                      Если вы не вызвали удаление объекта сами — вы можете быть уверены, что никакой там «сборщик мусора» не решит внезапно это сделать за вас в самый неподходящий момент
                                                      Это в чистом C++. Мало кто пишет без библиотек и фреймворков. Навскидку, объекты могут быть убиты:
                                                      1. Умными указателями
                                                      2. Всякими parent-like конструкциями из объектов
                                                      3. Чем-то вроде сигналов-слотов в Qt
                                                      4. Могут убиться сами по воле создателя какого-нибудь особо «умного» базового класса.
                                                    • +3
                                                      Мне вообще кажется, что без этой презентации Скотта статья будет неполной.
                                                      • 0
                                                        Во-первых, никаких точек следования больше нет.
                                                        Во-вторых, то что во многих местах стандарт оставляет за реализацией право на некоторую свободу в действиях (при условии неизменности конечного результата) закулисной игрой назвать сложно.
                                                        • +4
                                                          Плюс не стоит забывать о тоннах неявно генерируемых функций — все эти конструкторы, деструкторы, операторы копирования по умолчанию трудно не назвать «закулисными играми».
                                                        • –26
                                                          Соответственно, что-то в этом языке сделано правильно. Почему-то он плывёт. Давайте разбираться почему.

                                                          До этого места в статье всё было хорошо и интересно, особенно эта вставка про затонувший корабль.
                                                          Однако стоило дойти до этого места — и тут же началось бесконечное перечисление вещей, «сделанных правильно», из-за которых он «плывёт».
                                                          И вот тут у меня подозрение, что слишком много киллер-фич, чтобы это было правдой.
                                                          Причём на каждую киллер-фичу отвели всего по три предложения.
                                                          Если бы автор РЕАЛЬНО нашёл бы киллер-фичу, которая сделана правильно и почему корабль плывёт — она скорее всего была одна. И описана была намного более подробно — например, в сравнении с другими языками, которые не плывут дальше и другими языками, которые плывут.
                                                          Поэтому скажу словами Станиславского: «НЕ ВЕРЮ!!»
                                                          • –13
                                                            Ни один из минусующих не хочет пояснить свою позицию словами?
                                                            • +30
                                                              Вы, наверное, «вылезли» из мира эппов для мобилок или «крутых» веб аппликаций, где самый крутой и важный (модный?) фактор, определяющий успех, это КИЛЛЕР-фича. Вот вам прям вынь да положь эту килллер-хрень, иначе никто пользоваться не будет.
                                                              Странно, что до Вас не доходит, что серьёзная система это, прежде всего, комбинация разных факторов и возможностей, а не одна конкретная «киллер-хрень»
                                                              • +7
                                                                У С++ есть такая Киллер-Фича, на самом деле.
                                                                Он супер-мощный и одновременно супер-портабельный.

                                                                Не то, что всякая платформо-зависимая всячина, начиная с Objective-C (гибрид утконоса и ехидны си и смолтока, получивший распространение только в Apple) и кончая явой и дотнетом (для которых нужно тащить жирный рантайм — попробуйте-ка запихнуть его в микроконтроллер).
                                                                • 0
                                                                  Таки зависит от мк — в некоторые влазит (понятно, что не полный). Вообще java не такой уж слабый язык в плане embed-устройств.
                                                                  • +1
                                                                    Вы наверное удивитесь, но Java рантайм стоит даже в вашей SIM-карте: ru.wikipedia.org/wiki/JavaCard
                                                                    • +10
                                                                      Как человек реально работавший с этим чудом в своё время могу ответственно вам заявить: никакой Java'ой там и не пахнет.

                                                                      То есть это даже не Dalvik, программы для которого хоть чем-то напоминают Java. Это — вещь куда «круче». Если её с чем-то и сравнивать — то разве что с BASIC'ом. Причём не с VB.NET и даже не с просто VB, а с тем самым BASIC'ом с первых восьмибитных персоналок.

                                                                      Сами подумайте: в этой Java отсутствует сборщик мусора. То есть совсем. Думаете взамен дали delete? Ага, щаз. Его — тоже нету. То есть вы за всё время жизни программы можете создать фиксированное число объектов ибо умирать они не умеют в принципе. И если вы думаете, что вы можете отработать запрос и умереть, то я вас, опять-таки разочарую: программа «запускается» при записи её на карточку и считается «работающей» пока её не сотрут.

                                                                      P.S. Разумеется всяких излишеств типа строк там или целых чисел тоже нету. Short'ы есть, да. Байты ещё. Живите — ни в чём себе не отказывайте. Java, ага.
                                                                      • 0
                                                                        Вы путаете Java как язык программирования, и платформы Java. Последних существует несколько: Java SE, Java EE, Java ME, Java Card и пр. Естественно разработка под каждую из них кардинально отличается, но это все равно Java.
                                                                        • +9
                                                                          Судя по описанию выше, это настолько малое подмножество JVM, что общее там по факту только название.
                                                                          • +1
                                                                            JVM там нету совсем. Я не зря Dalvik вспомнил. JavaCard устроена точно также: сначала компилируем в байткод, потом конвертрируем результат. Если в байткоде что-то страшное встретилось (ну там кто-то synchronized написал или float себе завёл) конвертор ругается. Меня всегда удивляло, что во всех наездах на Android (типа он устроил фрагементацию в дружном мире Java) никто не вспоминает про JavaCard.
                                                                    • +2
                                                                      и кончая явой

                                                                      попробуйте-ка запихнуть его в микроконтроллер


                                                                      Взгляните:
                                                                      mcujavasource.sourceforge.net/ru/index.xhtml
                                                                      dmitry.gr/index.php?r=05.Projects&proj=12.%20uJ%20-%20a%20micro%20JVM
                                                                      en.wikipedia.org/wiki/Java_Card

                                                                      а вообще, куча есть штук. Кажется, даже плата есть специальная учебная, которая программируется на C# и жует .NET-овский байткод.
                                                                      • +1
                                                                        www.tinyclr.com
                                                                        www.netduino.com
                                                                        www.ghielectronics.com/technologies/netmf

                                                                        про java — она, например в каждой сим-карте присутствует практически, не считая кофеварок, стиральных машинок, и еще кучи вещей с микроконтроллерами
                                                                        • +1
                                                                          >>платформо-зависимая всячина, начиная с Objective-C

                                                                          Вы удивитесь, но реализации GNU под этот язык есть под Линукс и Windows. Да смачных фишек от еппл у них нет (АРЦ — на пример, ЮАй кит в библиотеке поставке).
                                                                      • –7
                                                                        Я бы тоже минуснул, если бы мог.
                                                                    • –11
                                                                      Давайте посмотрим вот на это:
                                                                      f(x);
                                                                      Вроде бы всё ясно ...


                                                                      Да, есть и еще интересный пример
                                                                      #include <iostream>
                                                                      
                                                                      double i = 3.14;
                                                                      
                                                                      void f() { std::cout << i << std::endl; }
                                                                      
                                                                      int main()
                                                                      {
                                                                        int(i) = 20;
                                                                        std::cout << i << std::endl;
                                                                        f();
                                                                        return 0;
                                                                      }
                                                                      

                                                                      Казалось бы тоже все ясно. И человек малознакомый с тонкостями C++ мог бы написать такой код в надежде на преобразование типа (в данном случае double к int), но тут не приведение типа, а объявление новой переменной, хотя и странно выглядящее…
                                                                      • +10
                                                                        Та ну, это Вы накрутили.
                                                                        Всем и так ясно, что локальная переменная i типа int, скрывает глобальную переменную i типа double и cout в main выведет 20, а cout в f выведет 3.14, поскольку он понятия не имеет о какой-то локальной переменной в другой функции :)
                                                                      • –2
                                                                        -
                                                                        • +18
                                                                          А мне статья не понравилась и вот почему.
                                                                          1. Заявление о том, что самая важная фича в Си++ — это декструкторы, на мой взгляд весьма неочевидно. Может быть это и так, но в статье данная тема не раскрыта, притом, что заявление очень и очень громкое.
                                                                          2. В статье неявно используется тезис, гласящий, что Си++ до сих пор на плаву главным образом потому, что он обладает такими хорошими свойствами. Совсем неочевидно, что этот тезис верен. Лично я склонен считать, что в плавучесть этого языка вносят весомый вклад также и внешние факторы, и история. Например, язык Си является куда более распространенным, но почти никто не сомневается, что он менее удобен для большинства задач, нежели Си++.
                                                                          3. В главе «Шаблоны» есть проблемы со связностью. Скорее всего предложения этой главы как-то связаны друг с другом, но мне, как читателю, эта связь кажется туманной. Стоило бы объясниться яснее.
                                                                          4. Толи я что-то не понял, толи диаграммы в некоторых вопросах конфликтуют с текстом. Заявляется, например, что Си++ — на втором месте по вкладу в OS. А на диаграммах прямо над этим текстом Си++ на третьем месте после Java и Си. Тут видимо тоже есть смысл, но он раскрыт плохо. Необходимы дополнительные комментарии автора.

                                                                          Общее впечатление о статье также плохое. Анализа мало, знамён и воодушевляющих речей много. А хотелось бы именно анализа, тема-то интересная…
                                                                          • +9
                                                                            1. Личное мнение Скотта, ничего поделать не могу. Я бы как самую важную фичу определил бы вцелом ООП (классы, уровни доступа, наследование, виртуальные функции, деструкторы), но Скотт сказал как сказал.
                                                                            2. В статье используется тезис, что С++ на плаву в том числе благодаря набору таких хороших свойств. Понятное дело, что есть и исторические факторы и вклад сильных программистов и подковёрные игры корпораций и много чего ещё.
                                                                            3. Перечитал раздел о шаблонах. Несвязанности не нашел. Аппарат разбился из-за несогласованности типов в коде. С++ даёт возможность контроля типов, один из инструментов для этого — шаблоны. Программу того аппарата можно было спроектировать так, что ошибка несогласованности систем мер и весов были бы невозможны.
                                                                            4. Перечитайте названия диаграмм, вклад в опенсорс — под текстом и там С++ второй.

                                                                            Анализа мало, поскольку до этого Скотт много выступал с чисто техническими докладами, где дотошно разбирал всякие моменты языка, а этот доклад был такой «евангелический».
                                                                            • 0
                                                                              А не могли бы вы дать ссылки на технические доклады, если они имеются?
                                                                            • –4
                                                                              Знаю c++ мало (никогда не программировал на нём коммерческий код), но:
                                                                              1. Деструкторы — вещь безумно полезная… на фоне проблем языков, существовавших до появления c++. В современном мире есть аналоги, которые могут успешно конкурировать. Он на этом акцент делал и вы опустили / он сознательно манипулировал фразой?
                                                                              3. Не очень знаком с шаблонами (и откровенно плохо их помню) в c++, но что-то не очень представляю как они должны спасти, если тип — везде масса. Либо же предполагается что-то вроде java-шного GregorianCalendar, который по умолчанию используется и остальных при явном определении (ну и даты, соответственно автоматически транслируются между календарями)?
                                                                              4.
                                                                              С++ на втором месте по количеству кода в opensource (12%) и этот процент растёт год от года.
                                                                              — на скриншоте сразу над этой фразой явно видно, что c++ постоянно занимает 3ье место (один раз 2ое), при этом на графике слева видно, что не растёт, а даже наоборот — падает.
                                                                              • 0
                                                                                Для непонятливых, смотреть надо на картинку что ниже, а ещё желательнее читать что написано на картинках.
                                                                                • 0
                                                                                  — Качество картинок не способствует их внимательному чтению,
                                                                                  — обычно поясняющий текст пишется под картинкой, в противном случае (когда картинка идёт под поясняющим текстом) в конце текста следует ставить ":". Если автор не хочет соблюдать стандарты форматирования — он получит недопонимания и это логично.
                                                                                • +5
                                                                                  >> Деструкторы — вещь безумно полезная… на фоне проблем языков, существовавших до появления c++. В современном мире есть аналоги, которые могут успешно конкурировать.

                                                                                  Например? Из известного мне это только scope guards, но они не покрывают все сценарии. GC аналогом не является (если вы думаете, что деструкторы нужны для управления памятью — это уже принципиальная ошибка).

                                                                                  >> Не очень знаком с шаблонами (и откровенно плохо их помню) в c++, но что-то не очень представляю как они должны спасти, если тип — везде масса.

                                                                                  Типом в данном случае будет не просто масса, а «масса в кг», и в случае использования масс с разными единицами, нужно будет явно приводить что-то к чему-то. В C++ это делается на шаблонах, и есть готовая реализация в виде Boost.Units. Вот кусок примера из их документации:

                                                                                  quantity<si::volume>    vs(1.0*pow<3>(si::meter));
                                                                                  quantity<cgs::volume>   vc(vs);
                                                                                  quantity<si::volume>    vs2(vc);
                                                                                  
                                                                                  quantity<si::energy>    es(1.0*si::joule);
                                                                                  quantity<cgs::energy>   ec(es);
                                                                                  quantity<si::energy>    es2(ec);
                                                                                  
                                                                                  quantity<si::velocity>  v1 = 2.0*si::meters/si::second,
                                                                                                          v2(2.0*cgs::centimeters/cgs::second);
                                                                                  }
                                                                                  


                                                                                  Соответственно, при умножении расстояния на время, например, у вас получается именно скорость, а не что-то иное — причем именно в той системе измерения, в которой были входные параментры. Если это разные системы, то вам придется написать явное приведение через static_cast, чтобы однозначно задать, в чем должен быть результат.

                                                                                  В F# аналогичная, но более простая фича есть на уровне языка — возможно, с этим будет проще разобраться.
                                                                                  • +1
                                                                                    Например, для организации RAII не нужны никакие специальные конструкции, кроме первосортных функций. Их достаточно для того, что бы организовать RAII.

                                                                                    Тем не менее, в C++ изначально не было первосортных функций, зато была придумана искусственная конструкция, имеющая смысл только в C++ версии ООП. Хотя язык гибридный и не принуждает к использованию ООП.
                                                                                    • +3
                                                                                      Вы имеете в виду аналог using в C#, который принимает функцию-параметр в качестве «тела»? Ну, для начала, это не эквивалент уже хотя бы потому, что в нем нельзя сделать return из внешней функции, или break из внешнего цикла. Но даже если забыть про это, каким образом это поможет вам в случае графа объектов, где при уничтожении владельца должны автоматически уничтожаться и все зависимые объекты?
                                                                                      • 0
                                                                                        Ну, для начала, это не эквивалент уже хотя бы потому, что в нем нельзя сделать return из внешней функции, или break из внешнего цикла.
                                                                                        Тогда добавляем ещё first-class escapes (возможность передать в функцию объект, с помощью которого можно сделать return/break в нужное место) и получаем… Scheme.
                                                                                        • 0
                                                                                          Но деструкторы полностью все равно не заменили (деревья придется обходить вручную).
                                                                                          • 0
                                                                                            Настолько же вручную, насколько поддержание дерева придётся «вручную» прописывать в деструкторе владельца или типе поля, хранящего зависимые объекты.
                                                                                            • 0
                                                                                              В деструкторе владельца не нужно явно вызывать деструкторы дочерних объектов.

                                                                                              А тип поля (если забыть про прочий профит от типизации) прописывается не ради деструктора, а ради конструктора — т.е. указывает на то, объект какого вообще типа мы создаем. Этого вы не избежите и в языке с динамической типизацией. А автоматический вызов деструктора там прилагается бесплатно, в отличие от.
                                                                                              • 0
                                                                                                Согласен. В общем, полноценные объекты функций + «аналог using» (+ dynamic-wind) позволяют получить конструкцию «выполнить X на входе в лексический блок и Y на выходе», для чего в Си++ RAII чаще всего и используется (всякие гварды, локи и т. д.)

                                                                                                Но вот какой-нибудь shared_ptr — тоже пример RAII, — на них не соорудить. Или ваше дерево объектов (если только сам объект-владелец не убивается при выходе из блока).
                                                                                                • +1
                                                                                                  Как убивается объект-владелец, как раз не важно. Пусть даже это будет выход из блока. Но как он будет в свою очередь автоматически убивать то, чем владеет?

                                                                                                  shared_ptr соорудить как раз вполне можно, но там будут ровно те же ограничения — если у вас есть только «using», то все ваши shared_ptr'ы должны будут быть локальными в каком-то блоке, или же на них придется где-то явно вызывать release.
                                                                                    • –2
                                                                                      Первоочерёдная задача деструктора — корректное закрытие объекта (потоков). С этим gc + обвязка вполне справляются. Вторичная — корректное «завершение» работы объекта может быть легко выполнено архитектурно — не вижу проблем с этой стороны. Хотя первое время, согласен, тяжело переориентироваться на такой подход.

                                                                                      Окей, шаблоны из гуд (хотя того же можно достичь, опять же, архитектурно). Только такие примеры (расчёт скорости спутника) неплохо бы расписывать более явно (а не только для тех, кто язык итак знает). К тому же, как я уже говорил — там скорее архитектурная проблема (никто и не подумал, что масса / скорость может измеряться в других системах счисления).
                                                                                      • +4
                                                                                        >> Первоочерёдная задача деструктора — корректное закрытие объекта (потоков). С этим gc + обвязка вполне справляются.

                                                                                        Не справляется с этим GC. С памятью — да, но большинство других ресурсов (мьютексы, сокеты, хэндлы памяти etc) должны быть освобождены детерминистично, и GC здесь не помощник. Или под этим вы имеете в виду…

                                                                                        >> Вторичная — корректное «завершение» работы объекта может быть легко выполнено архитектурно — не вижу проблем с этой стороны.

                                                                                        В таком случае, объясните, как именно это «легко выполняется архитектурно» без ручного управления временем жизни в отсутствие деструкторов (и, как следствие, RAII).

                                                                                        >> Окей, шаблоны из гуд (хотя того же можно достичь, опять же, архитектурно)

                                                                                        Опять эта же непонятная фраза. Поясните на конкретном коде.

                                                                                        >> К тому же, как я уже говорил — там скорее архитектурная проблема (никто и не подумал, что масса / скорость может измеряться в других системах счисления).

                                                                                        Это именно та самая проблема, которая решается достаточной типизацией. Собственно, люди её решают так же, когда пишут единицы измерения после всех величин при рассчетах.
                                                                                        • 0
                                                                                          (2ой абзац) Посмотрите на AutoClosable интерфейс. Все необходимые операции завершаются вполне детерминистично. Аналог вторичного функционала деструкторов. Или для вас деструкторы — менее ручное, чем такое архитектурное решение?

                                                                                          (3ий абзац) Абсолютный аналог — даты. Вот есть java.lang.Date, который содержит UnixTimeStamp, а есть Calendar и его субклассы типа GregorianCalendar и прочих, которые умеют UTS переводить в дату на выбранном календаре. Это в общих чертах. Это и является архитектурным решением.
                                                                                          • +4
                                                                                            >> (2ой абзац) Посмотрите на AutoClosable интерфейс. Все необходимые операции завершаются вполне детерминистично. Аналог вторичного функционала деструкторов. Или для вас деструкторы — менее ручное, чем такое архитектурное решение?

                                                                                            Нет, это вполне нормально. Но, собственно, AutoClosable::close — это и есть деструктор. :)

                                                                                            С одним «но». Попробуйте на нем реализовать ситуацию, когда у вас не один объект, а дерево. Корень-то вы высвободите через AutoClosable автоматически, ок. А вот все «висящие» на нем объекты придется освобождать ручками в его close(). И так для каждого уровня дерева. Профит семантики плюсовых деструкторов в том, что они работают для всего, а не только для локальных переменных, причем автоматически — объявили переменную, получите вызов деструктора.

                                                                                            Кстати, вы, наверное, в курсе, что в C# аналогичная AutoClosable вещь называется IDisposable (и метод, соответственно, Dispose). Так вот там, C++/CLI отображает деструкторы ref-классов на Dispose в генерируемом коде. Но они сделали правильно, и оставили у них полную семантику плюсовых деструкторов — т.е. если в одном ref class есть поле типа другого ref class, и второй класс имеет деструктор (т.е. на уровне CLI — реализует IDisposable), то первый тоже автоматически получает IDisposable, который вызовет Dispose на дочерний объект.

                                                                                            >> Абсолютный аналог — даты. Вот есть java.lang.Date, который содержит UnixTimeStamp, а есть Calendar и его субклассы типа GregorianCalendar и прочих, которые умеют UTS переводить в дату на выбранном календаре. Это в общих чертах. Это и является архитектурным решением.

                                                                                            Это ничем не отличается от решения с шаблонами, кроме того, что вы напишете больше кода руками. Т.е. с вашим подходом вы вручную будете определять классы типа SiLength, SiTime и SiVelocity, и вручную же описывать, что SiLength/SiTime дает SiVelocity. А потом отдельно делать то же самое для ImperialLength etc, и опять же руками описывать преобразования. В Boost.Unit типа «скорость» как такового нет, это составной шаблонный тип из расстояния и времени, что отражает его единицу измерения (м/с) — ни одной строчки кода специально для него писать не надо было. Аналогично, из скорости при делении на время точно так же автоматически получается ускорение, и т.д. А для разных систем, достаточно задать преобразование между базовыми единицами данной величины один раз, и все производные также получают их автоматически. Т.е. если вы умеете переводить футы в метры, то это автоматически будет применимо к переводу из фт/с в м/с.

                                                                                            Собственно, шаблоны вообще — это штука, абсолютно перпендикулярная тому, что вы назвали архитектурой. Они просто позволяют описывать куда более сложные решения с меньшим объемом кода.
                                                                                            • 0
                                                                                              Но, собственно, AutoClosable::close — это и есть деструктор. :)

                                                                                              Тут вы не правы (если мы говорим про Java) — close всего лишь закрывает длительную операцию. Т.е. фактически освобождается какой-то занятый ресурс, но не уничтожается сам объект (что для деструктора является обязательным условием).

                                                                                              Вопрос с деревом решается как и в деструкторе (хотя мне не очень понятна область применения дерева относительно ресурсов) — каждый объект закрывает дочерние объекты и закрывается сам.

                                                                                              IDisposable и AutoClosable всё-таки разные (совсем) вещи. Все объекты в Java имеют метод dispose. Но есть причины по которым его не используют — JVM не гарантирует время вызова этого объекта. Поэтому, в отличие от C++ в Java различают ресурсы, которые надо закрывать детерминистично (мониторы, потоки, ...) и недетерминистично (сами объекты). Для первых необходимо использовать архитектурные решения, для вторых — можно оставить на совести GC.
                                                                                              • +2
                                                                                                >> Тут вы не правы (если мы говорим про Java) — close всего лишь закрывает длительную операцию. Т.е. фактически освобождается какой-то занятый ресурс, но не уничтожается сам объект (что для деструктора является обязательным условием).

                                                                                                Это вопрос терминологии — само слово «деструктор» четко не определено. Например, в C# им зачем-то назвали finalizer, который вообще ни разу не детерминистичный. Я его использовал в смысле «обратная сторона инициализации, которая обеспечивает все гарантии в RAII».

                                                                                                Объект действительно не уничтожается ни в Java, ни в C#, но он, как правило, переходит в состояние «невозможно использовать», когда все методы кидают исключения — причем в большинстве случаев это one-way ticket, т.е. вернуть его обратно в нормальное состояние невозможно. В C# это вообще прописано в контракте Dispose. С моей т.з, важное в деструкторе то, что он освобождает все ресурсы, требующие детерминистичного высвобождения (в данном языке). Соответственно, в плюсах это включает в себя и память, а в Java и C# — нет, поэтому и есть эта разница со временем жизни. Но на практике для RAII она совершенно не важна.

                                                                                                >> Вопрос с деревом решается как и в деструкторе (хотя мне не очень понятна область применения дерева относительно ресурсов) — каждый объект закрывает дочерние объекты и закрывается сам.

                                                                                                С деструкторами каждый объект не должен явно закрывать дочерние объекты, его деструктор автоматически позовет их деструкторы. Более того, такой деструктор вообще не надо определять (если у объекта нет своей логики освобождения ресурсов) — он сам появится автоматически при наличии полей, которые надо чистить. Это принципиальная разница с close(), который надо будет руками определять, и руками же звать на всех владеемых объектах.

                                                                                                Дерево с ресурсами — ситуация вполне типичная. Например, в UI-фреймворках, дерево обычно соответствует дереву виджетов, а их ресурсы — это оконные хэндлы (т.е. HWND на Win32, например). И при убивании корня дерева (обычно, окна верхнего уровня), нужно освободить вот это все.

                                                                                                Еще часто бывает ситуация, когда сам по себе граф объектов не представляет ресурсы, но у каждого объекта во владении есть еще один или несколько несвязанных объектов, которые надо прибить — например, по файлу на нос. Соответственно, у объектов в дереве нужен деструктор, просто для того, чтобы он позвал таковой на файле. Вообще, деструкторы — заразная штука, в прямом смысле :)

                                                                                                >> IDisposable и AutoClosable всё-таки разные (совсем) вещи. Все объекты в Java имеют метод dispose. Но есть причины по которым его не используют — JVM не гарантирует время вызова этого объекта.

                                                                                                Вы, кажется, путаете здесь dispose (которого в Java таки нет — точнее есть, но только у отдельных классов, например в AWT), и finalize (который есть и в C#, и в Java у всех объектов, но в C# для него используется особый синтаксис, хотя на уровне CLR там все равно Finalize). IDisposable — это именно самый что ни на есть прямой эквивалент AutoClosable, интерфейс и метод для детерминистичного высвобождения ресурсов, в сочетании с конструкцией языка для автоматического вызова этого метода.
                                                                                        • +1
                                                                                          Окей, шаблоны из гуд (хотя того же можно достичь, опять же, архитектурно)


                                                                                          Это как же Вы архитектурно достигнете:
                                                                                          а) вычислений во время компиляции
                                                                                          б) автоматической генерации кода
                                                                                          ?
                                                                                          • 0
                                                                                            И совсем забыл про в) полиморфизм на этапе компиляции.

                                                                                            Это все к тому, что шаблоны в C++ ну оочень мощный интсрумент.
                                                                                            • 0
                                                                                              а) и чем же так полезны вычисления во время компиляции?
                                                                                              б) это можно делать и в рантайме, но в общем-то… а в чём прикол?
                                                                                              в) тот же вопрос — какие бонусы получаем?
                                                                                              На примерах показать можете?
                                                                                              • 0
                                                                                                Я смотрю Вы никогда не работали с критичным к производительности кодом.
                                                                                                Делать некоторые вещи в рантайме может оказаться непомерно дорогим удовольствием. При этом нам по прежнему нужен толерантный к изменениям код. Тут на помощь приходят и статический полиморфизм (вспоминаем концепцию «стратегий» у Александреску А. «Современное проектирование на с++»), и вычисления на этапе компиляции.
                                                                                                • 0
                                                                                                  Вы же согласны, что даже такой простой механизм, как позднее связывание, тоже может оказаться дороговатым удовольствием в некоторых случаях?
                                                                                        • +1
                                                                                          1. Деструкторы настолько полезная вещь, что даже в яве сперва от них отказались («память больше не ресурс»), а потом вдруг спохватились и вернули scope guard'ы в объектном стиле (спец.метод у «дежурного» объекта), а не в древнем процедурном __try — __finally

                                                                                          Да, в плюсах неудобно писать процедурные scope guard'ы «по месту», но в бусте такая штука появилась десять лет назад, а с лямбдами там и буст не особо нужен.

                                                                                          3. Шаблоны, как и вообще параметризованные типы (привет хаскеллу) позволяют делать разные чудеса. Но для понимания чудес надо всё-таки их изучить.
                                                                                          Один вариант — это, в стиле Ады, просто запретить неявные приведения типов «масса-в-кг» и «масса-в-фунтах»
                                                                                          Другой — реализовать это приведение корректно, с пересчётом значения.
                                                                                          Тогда
                                                                                          m_in_kg x;
                                                                                          m_in_pnd y;
                                                                                          x = y;
                                                                                          будет превращаться в
                                                                                          x.value = y.value * 0.454
                                                                                        • 0
                                                                                          >> С++ даёт возможность контроля типов, один из инструментов для этого — шаблоны. Программу того аппарата можно было спроектировать так, что ошибка несогласованности систем мер и весов были бы невозможны.

                                                                                          Кто мешает завести различные типы для систем мер и весов, и зачем для этого применение шаблонов?
                                                                                          • +1
                                                                                            Замучаетесь вручную задавать типы для производных единиц, и преобразования между ними же в разных системах.
                                                                                        • +6
                                                                                          Про то, что самая важная фича — деструкторы, Скотт заявил, сразу же пояснив, что имеется в виду то, что именно деструкторы сделали возможной концепцию RAII, которую он называет UNDO-механизм.
                                                                                          • –3
                                                                                            Мы как-то к коллегами незлобно спорили о деструкторах и их важности.
                                                                                            Я, как приверженец жавы, спросил — какую логику программы, если не считать освобождения ресурсов (памяти и прочих хендлов) можно/нужно поместить в деструктор? И если бизнес-логики нет — то не проще ли поручить деструкцию железяке?
                                                                                            Внятного ответа на этот вопрос я не получил, коллеги мялись насчет того, что память надо сразу освобождать, а в деструктор можно вставить логгирование.
                                                                                            • +5
                                                                                              Хех, в деструктор можно много чего вставить. К примеру QMutexLocker в деструкторе разблокирует мутекс.
                                                                                              • –13
                                                                                                Это как в анекдоте про супружество: «Брак нужен людям чтобы сообща решать проблемы, которые бы не возникли в случае, если бы брака не было».

                                                                                                Разблокировка мутекса в деструкторе — признак того, что блокировка его происходит в конструкторе.
                                                                                                Не проще тогда отнаследоваться от мутекса тем или иным способом?

                                                                                                (Да, есть еще варианты вольного обращения с мутексом туда-сюда и на всякий случай в деструкторе разблокировать — это говорит о лапше в алгоритме)
                                                                                            • +7
                                                                                              Самый наглядный пример: класс LongOperation, без единого метода, в конструкторе изменяет курсор на песочные часы, в деструкторе изменяет обратно на стрелку.
                                                                                              Тогда в длительно работающей функции достаточно объявить переменную типа LongOperation где-то в начале, и больше её не трогать.
                                                                                              И можно быть уверенным, что курсор изменится обратно при выходе из функции любым способом (return из середины, исключение, что угодно).
                                                                                              • –2
                                                                                                Я несколько не о том.
                                                                                                Понятно, что раз объект создается и инициализируется, он при уничтожении должен как-то вернуть исходное состояние системы. Но нужно ли программисту для этого писать руками деструкторы? Конкретно в вашем же примере «достаточно объявить» — и остальное сделает за вас компилятор. Я не хочу разводить холивары — жава и с++ — я обоими этими яыками пользуюсь. И люблю их оба по-своему.
                                                                                                • +5
                                                                                                  С точки зрения пользователя LongOperation — действительно, «всё сделает за вас компилятор». Как раз потому, что автор LongOperation написал руками деструктор.

                                                                                                  Мой предыдущий комментарий был ответом на ваш конкретно заданный вопрос: «какую логику программы, если не считать освобождения ресурсов (памяти и прочих хендлов) можно/нужно поместить в деструктор?»

                                                                                                  Память компилятор может освободить и сам, но всё более высокоуровневое состояние (курсоры, мутексы, сетевые соединения, блокировки в базе данных) освобождать придётся программисту. Руками, да.
                                                                                              • +1
                                                                                                «Надо выбирать себе умных коллег»
                                                                                                В.Маяковский
                                                                                                • +1
                                                                                                  В дестркуторе можно выполнить любой код, который нужно гарантированно выполнить при выходе из области видимости ( запись в лог, в файл, освобождение ресурса, отправка сообщения по сети ).
                                                                                                  • 0
                                                                                                    >>В дестркуторе
                                                                                                    >>отправка сообщения по сети
                                                                                                    За такое по рукам надо бить
                                                                                            • +10
                                                                                              Шаблоны настолько замечательная вещь, что можно потратить пару дней, пока напишешь абстрактный код, работающий так, как надо.

                                                                                              А еще ошибки в коде с шаблонами очень замечательные. Длинные.

                                                                                              Утверждают, что для следующего кода компилятор выдаст ошибок на примерно 16 килобайт.

                                                                                              #include <vector>
                                                                                              #include <algorithm>
                                                                                              int main()
                                                                                              {
                                                                                                  int a;
                                                                                                  std::vector< std::vector <int> > v;
                                                                                                  std::vector< std::vector <int> >::const_iterator it = std::find( v.begin(), v.end(), a );
                                                                                              }
                                                                                              


                                                                                              • 0
                                                                                                У меня только 2.5 Кб) Может от компилятора зависит (пробовал на VS 2013 Pro Update 2).
                                                                                                лог ошибки
                                                                                                1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(3026): error C2678: binary '==': no operator found which takes a left-hand operand of type 'std::vector<int,std::allocator<_Ty>>' (or there is no acceptable conversion)
                                                                                                1> with
                                                                                                1> [
                                                                                                1> _Ty=int
                                                                                                1> ]
                                                                                                1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\system_error(410): could be 'bool std::operator ==(const std::error_condition &,const std::error_code &) throw()'
                                                                                                1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\system_error(402): or 'bool std::operator ==(const std::error_code &,const std::error_condition &) throw()'
                                                                                                1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\exception(507): or 'bool std::operator ==(const std::exception_ptr &,std::nullptr_t)'
                                                                                                1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\exception(502): or 'bool std::operator ==(std::nullptr_t,const std::exception_ptr &)'
                                                                                                1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\exception(497): or 'bool std::operator ==(const std::exception_ptr &,const std::exception_ptr &)'
                                                                                                1> while trying to match the argument list '(std::vector<int,std::allocator<_Ty>>, const int)'
                                                                                                1> with
                                                                                                1> [
                                                                                                1> _Ty=int
                                                                                                1> ]
                                                                                                1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(3045): see reference to function template instantiation '_InIt std::_Find<_InIt,_Ty>(_InIt,_InIt,const _Ty &,std::false_type)' being compiled
                                                                                                1> with
                                                                                                1> [
                                                                                                1> _InIt=std::vector<int,std::allocator> *
                                                                                                1>, _Ty=int
                                                                                                1> ]
                                                                                                1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(3054): see reference to function template instantiation '_InIt std::_Find<std::vector<int,std::allocator<_Ty>>,_Ty>(_InIt,_InIt,const _Ty &)' being compiled
                                                                                                1> with
                                                                                                1> [
                                                                                                1> _InIt=std::vector<int,std::allocator> *
                                                                                                1>, _Ty=int
                                                                                                1> ]
                                                                                                1> c:\users\i.zimaev\documents\visual studio 2013\projects\consoleapplication1\consoleapplication1\source.cpp(13): see reference to function template instantiation '_InIt std::find<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<std::vector<int,std::allocator<_Ty>>>>>,int>(_InIt,_InIt,const _Ty &)' being compiled
                                                                                                1> with
                                                                                                1> [
                                                                                                1> _InIt=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<std::vector<int,std::allocator>>>>
                                                                                                1>, _Ty=int
                                                                                                1> ]
                                                                                              • +38
                                                                                                потому, что С++ не тонет
                                                                                                • +4
                                                                                                  Есть итересное обсуждение Generate the longest error message in C++ из которого можно почерпнуть много рецептов на тему «как прострелить себе что-нибудь побольнее».
                                                                                                  • +2
                                                                                                    О б-же, это восхитительно.
                                                                                                    Больше всего понравился пример, по заявлениям общественности сегфолтящий gcc:
                                                                                                    #define A(s) s##s##s##s##s##s##s##s
                                                                                                    #define B(s) A(s##s##s##s##s##s##s##s)
                                                                                                    #define C(s) B(s##s##s##s##s##s##s##s)
                                                                                                    #define D(s) C(s##s##s##s##s##s##s##s)
                                                                                                    #define E(s) D(s##s##s##s##s##s##s##s)
                                                                                                    #define F(s) E(s##s##s##s##s##s##s##s)
                                                                                                    #define G(s) F(s##s##s##s##s##s##s##s)
                                                                                                    a G(foo)
                                                                                                    
                                                                                                  • +26
                                                                                                    В не очень отдаленном будущем человечество будет колонизировать другие планеты — мне всегда было интересно, на каком языке будет написан бортовой софт звездолетов))

                                                                                                    Ну а если серьезно, то все это холивар от начала до конца! Почему С++ не утонул — ну а почему PHP не утонул, и живее всех живых, это показатель крутизны языка?? Мир IT — это сложнейшая экосистема, где действует множество факторов, в т.ч. имеющие к фичам языка весьма опосредованное отношение (legacy-код, всякие коммерческие интриги корпораций, порог вхождения в язык и многое другое).
                                                                                                    Почему визуальные языки так и не взлетели? Почему о невероятной мощи функциональных языков из поколения в поколения ходят легенды, но за эти годы (!!!) никто так и не построит на них крутой бизнес, оттяпав приличный кусок рынка, создав более-менее ощутимое количество рабочих мест? Почему существуют целые кланы Microsoft'о-ненавистников, которые задачу типично Enterprise сектора (много UI, бизнес-логики, базы, сети) будут до посинения кодить на C++, хотя именно для этого случая C# просто идеален, и позволил бы решить задачу в разы быстрее? Почему есть те, кто просто ненавидит Паскаль, или наоборот, поклоняется Вирту, и верит в гуляющую по сети байку про то, что С++ создан как шутка, и писать на нем невозможно?
                                                                                                    Мир людей не идеален, часто далек от логики, и нам предстоит еще много работы чтобы программисты могли ПРОСТО ПИСАТЬ КОД, забыв о технологических заморочках, проблемах кроссплатформенности, неподъемном синтаксисе, холиварах «какой язык лучше» и пр.
                                                                                                    • 0
                                                                                                      Толи у Винджа в «Пламени над бездной», толи у Симмонса в «Гиперионе» есть пассаж, что в далеком космическом будущем в основе программ, на которых работает искусственный интеллект, лежит код древней операционной системы Windows. ^)
                                                                                                      • 0
                                                                                                        Вообще-то, емнинмс, юникс. Было там что-то про счетчик времени в nix-формате.
                                                                                                        • 0
                                                                                                          Да, точно. Отсылка на unix-time там была.
                                                                                                        • 0
                                                                                                          У Винжа в «Глубина в небе» (приквел «Пламени над бездной») была остылка на софт из нашего времени, но, на сколько я помню, там была речь не про Windows, а про что-то из Unix. Кажется там было что-то от сокетов, могу ошибаться. И еще там была самая высокооплачиваемая профессия «программист-археолог» )
                                                                                                          • +3
                                                                                                            Фам Нювен несколько лет провел, обучаясь программировать и исследовать. Программирование восходило к началу времен. Как та навозная куча за замком отца. Когда ее промыло ручьем на десять метров в глубь, обнаружились искореженные корпуса машин – летающих машин, как говорили крестьяне, еще от тех великих дней колонизации Канберры. Но та навозная куча была чистой и свежей по сравнению с тем, что лежало в локальной сети «Репризы». Были программы, написанные пять тысяч лет назад, когда человечество еще не покинуло Землю. И самое чудесное (самое ужасное, как говорила Сура) было то, что, в отличие от бесполезных обломков прошлого Канберры, эти программы все еще работали! И через миллион миллионов запутанных нитей наследования многие из старейших программ все еще выполнялись во внутренностях системы Кенг Хо. Например, методы слежения за временем у торговцев. Поправки вносились неимоверно сложно – но на самом дне лежала крошечная программа, которая гоняла счетчик. Секунду за секундой отсчитывала система Кенг Хо с того момента, как нога человек ступила на Луну Старой Земли. Но если приглядеться еще пристальнее… начальный момент был миллионов на сотню секунд позже; момент «ноль» одной из первых компьютерных операционных систем Человечества.
                                                                                                            • 0
                                                                                                              Спасибо за цитату.
                                                                                                          • +7
                                                                                                            Почему С++ не утонул — ну а почему PHP не утонул, и живее всех живых, это показатель крутизны языка??
                                                                                                            Вы не поверите, но да, именно так.

                                                                                                            Только у него другая киллер-фича. PHP — это язык всеми фибрами заточенный на то, чтобы говнопрограммисты писали на нём говнокод. Или, если быть точным, чтобы люди не умеющие программировать могли-таки сваять нечто, что будет выдавать правильный результат в случае если пользователь будет аккуратен и не будет пытаться оную программу сломать.

                                                                                                            Разумеется обратной стороной медали является тот факт, что написать на нём хорошо работающую программу без ошибок и без уязвимостей куда сложнее, чем на Java или там C++, но с этим ничего не поделать, да. Это — прямое следствие его киллер-фичи.