Pull to refresh

Comments 95

Однако, 4.4 миллиона — это примерно треть населения Москвы. Много же людей пишут на C++!
Было бы интересно узнать, сколько приблизительно вообще профессиональных программистов в мире.
4.4 миллиона это больше половины населения некоторых стран )
Все в нашем мире относительно :)
Легендарный язык, на котором можно написать свою карманную вселенную и тут же ее уничтожить, параллельно уничтожив несколько параллельных реальностей.

Наверное самый холиварный язык в мире. И конечно же явления суть описана его автором:
«Есть всего два типа языков программирования: те, на которые люди всё время ругаются, и те, которые никто не использует.»
Bjarne Stroustrup.


П.С. Модули уже дайте а? Такой классный подарок будет.
А ещё можно взять нож и прострелить из него ногу в трёх местах :)
Поздравляю коллег! Лично я ни разу не пожалел что выбрал C\C++ как основные языки разработки
Всегда воспринимал С++ как нечто что было всегда, неизменным. Просто никогда не задумывался об этой стороне… А он, оказывается, развивался, не всё сразу построилось так как сейчас воспринимается привычным.

Интересно, почему ООП считалось медленным? В случае С++ весь overhead — только виртуальные функции (+1 операция на lookup), да и никто ведь в здравом уме не будет делать 100% функций виртуальными. Или за-за оверхеда по памяти так считалось?
Как я понимаю, это идёт из времён, «когда машины были большими, а программы маленькими», до эры «купите побольше оперативки и процессор поновее».

Когда бизнес систематически не платил за нефункциональные требования типа скорости софта и другие оптимизации, но платит за обновление вычислительного парка, сформировался эффект оптового производства железа, цена транзакции удешевилась за счёт быстрого железа, что нынче можно спрашивать «нужно ли волноваться из-за оверхеда».
Да нет, оверхед здесь побольше. Таблицы VMT, само создание/уничтожение классов. Сейчас этот overhead относительно мал, но давайте посмотрим на ресурсы персоналок в начале 90х… Начиная от набора регистров и до объема памяти.
Были времена, когда доступ к памяти выполнялся за десятки тактов, да и сейчас не всегда за один.
Ну вот в числодробилках всяких типа моделирования гидродинамики ООП до сих пор считается медленным. Я видел проект, в котором все было сделано через шаблоны, не было ни одной виртуальной функции. Легко прикинуть. К примеру, у вас система из дискретной равномерной сетки, N*M*L узлов, на каждой итерации надо обработать каждый узел, всего I итераций. В простейшем случае состояние узла на следующей итерации зависит от состояния его ближайших соседей (и его самого) на предыдущей итерации, положим 27 штук соседей ( включая сам узел). Итого получается вложенный цикл из 27*N*M*L*I итераций. Каждое из этих чисел N,M,L,I легко может быть порядка 1000 или 10000 (и это еще очень скромно). Итого 10^13-10^17 итераций. Если у вас в цикле будут вызовы виртуальных функций, то, скорее всего, вы это заметите. Конечно, зависит от того, что еще считается в цикле. В алгоритме, который я пробовал и имею в виду, добавление модного слоя абстракции в этом вложенном цикле на некоторые сущности через динамический полиморфизм сразу стоило 20% времени выполнения.
Разве числодробилки не под GPU пишут?
На супер-компьютерах обычно не GPU, вроде бы. Но очень много CPU.
На самом деле там есть и то, и другое.
А зачем ООП вообще нужно для моделирования узлов регулярной сетки? Тем более зачем нужны виртуальные функции?
О, это просто. Вот посмотрите, к примеру, на диаграмму классов уже упомянутого проекта с lattice-boltzmann method. Дело в возможности включать разную физику и численные методы. Можно обходить 15 ближайших узлов, можно 19, можно 27. Можно использовать разные разностные схемы. Можно моделировать однофазный поток, можно многофазный. Если многофазный, то с есть разные методы его моделирования (даже в рамках этого численного метода). Можно включать турбулентность, можно выключать. Можно использовать разные геометрии и граничные условия. В параллельном случае можно использовать разные топологии связей. И т.п. И так у вас набегает 2986 классов, хотя программа все еще, по сути, это вложенные четыре цикла с парой сложений и умножений внутри. К счастью, все комбинации классов обычно можно разрешать статически, поэтому можно обойтись статическим полиморфизмом.
И так у вас набегает 2986 классов, хотя программа все еще, по сути, это вложенные четыре цикла с парой сложений и умножений внутри


Похоже на оверинжениринг. Шаблонные функции с массивом флагов для настройки разных параметров алгоритма, как по мне, должно быть достаточно. Зачем ООП?
Ну как минимум у вас есть возможность подставлять в шаблонные параметры (класса или пусть и функции) не функцию за функцией, а группы функций (классы), что уменьшает число шаблонных параметров на порядок. Ну и в целом это метод борьбы со сложностью. Можно сгруппировать функции семантически; данные, интересные нескольким функциям, можно добавить в класс. Какие-то поля сделать приватными, чтоб никто их больше не видел и вы точно знали, что за пределами класса о них думать не надо. Делегировать что-то другому классу (конечно, можно делегировать просто разным функциям, но так у вас близкие функции уже сгруппированы, и понять делегирование намного проще). Или, к примеру, если у вас даже с классами шаблонных параметров функции или класса много, вы можете группировать какие-то из них в классы дальше. Приватные поля для кеширования сразу легко вводить и т.п. Короче, все, что пишут в книжках по ООП.
Бывает и такое!.. Подумал лучше — и понял, что порол какую-то полную чушь. Из-за глубоко укоренившейся привычки что ООП это иногда медленно, не смог принять для себя гипотезу об оптимальной виртуальности и сразу отбросил возможную объектную реализацию (именно из-за того, что помнил об оверхедах, а не из-за удобства проектирования… а ведь мне показалось что наоборот… беда).

Конечно, если бы виртуальные функции работали быстрее, объектная реализация была бы предпочтительнее. Не прав, приношу свои извинения.

P.S.: Попробовал прикинуть в коде как бы выглядела реализации без ООП — бр-р-р. Как вообще имитация полиморфизма может быть сделана без виртуальных функций? На свич-кейсах как-то не очень хорошо (вроде, по быстродействию это не будет давать весомого преимущества). Делать по гриду под каждый тип и выбирать на основе какой-то индексной таблицы — много памяти, да и тоже не совсем ясно как… Как такое делают на практике?
Ну, в общем случае — никак. Ибо виртуальные функции — это динамический полиморфизм, а шаблоны — это статический полиморфизм. Однако, применительно к данной задаче нам динамический полиморфизм вроде как и не нужен. Есть один большой алгоритм и его нужно посчитать для заданной параметризации, которая не будет меняться в процессе работы.

Можно привести такой пример. В динамическом полиморфизме есть интерфейс, а в статическом — концепт. Любой класс, реализующий заданный набор методов удовлетворяет этому концепту. Класс, удовлетворяющий концепту, передаётся на этапе компиляции, в то время как объект, удовлетворяющий интерфейсу, передавался бы на этапе выполнения.

В динамическом полиморфизме можно сделать условие на конкретный тип предоставленного объекта через RTTI и построить логику в зависимости от этого. В статическом можно сделать характеристический (traits) класс, у которого будут свои специализации под разные классы, которые он будет характеризовать.

Входной параметр функции — тоже динамический полиморфизм, по сути. Ведь функция работает по-разному в зависимости от значения параметра, пусть это и не имеет отношения к ООП. Вместо параметра можно использовать скалярный аргумент шаблона. В целом, любое поведение, доступное динамически, можно смоделировать статически, только при условии, что вся необходимая информация есть на этапе компиляции. И хотя выглядеть в С++ это может тяжеловато, компилятору с этим будет работать намного проще, нежели чем с виртуальными функциями, ибо ему сразу доступна вся возможная информация.
Типобезопасность и исключения дают ощутимый overhead.
Типобезопасность даёт оверхед только на времени компиляции.
То-то потоки на столько медленнее printf-ов.
Ну а шаблонный std::sort быстрее нешаблонного с компаратором вида void (*)(void const *, void const *)
UFO just landed and posted this here
В случае С++ весь overhead — только виртуальные функции (+1 операция на lookup)
* На этапе компиляции — это барьер для оптимизаций. Вы не можете встраивать через виртуальные функции
* На этапе выолпнения — проблемы с фетчингом кода. Я даже не знаю, сможет ли в таком случае отработать branch prediction, или будет полная останвка конвеера

Ну и да обычно это не важно
* На этапе компиляции — это барьер для оптимизаций. Вы не можете встраивать через виртуальные функции


Clang спокойно их девиртуализирует и инлайнит (там, где применимо).
Там, где применимо, виртуальные функции и не нужны
А минусовать-то зачем? Вы написали про барьер для оптимизаций, я написал, что этот барьер обходится.
UFO just landed and posted this here
UFO just landed and posted this here
С++ лучший язык для программирования компьютеров.
Не оч. удачная инфографика: сразу неочевидно, к какому из событий относится каждый из относительно больших фрагментов блеклого текста — расположенному над ним или сбоку от него, или это вообще «заметки на полях», к конкретным событиям не привязанные. И, да, было бы здорово видеть SVG- или HiDPI-вариант.
Интересно, а Линус Торвальдс праздновал этот день или для него это день траура?
С праздником, любимый язык! Я тебя никогда не брошу и, если понадобиться, буду бороться за тебя до конца!
Это очень мило, и я поддерживаю, но, всё же: если понадобится.
UFO just landed and posted this here
На всякий случай вот тут лежит рабочий драфт:
https://github.com/chriskohlhoff/asio-tr2
улучшенный STL с диапазонами (предложение Эрика Ниблера)

Это сейчас тоже в boost, Эрик адаптирует для стандарта здесь.
А мы, кстати, в честь 30-летия Cfront проверили. Публикация статьи запланирована на четверг.
Всего в мире сейчас более 19 млн разработчиков,

источник: www.3dnews.ru/912876

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

Сам С++ изучаю с 14 лет (т.е. уже 12 лет), из них 4 года — работаю С++-разработчиком. И все еще думаю, что не знаю его =)

С нетерпением из новых стандартов жду модулей.
Даже Страуструп где-то сам говорил, что не всё знает в C++.
C++ как квантовая физика, если думаешь, что знаешь, значит не знаешь :)
Насчет фразы про «большой шаг после С++98», подумалось
-С++98 — «мажорная» версия, первый стандарт, вроде как 1.0
-С++03 — апдейт, фиксы, вроде 1.1
-С++11 — долго-долго пилили, много-много фич, похоже на версию 2.0
-С++14 — быстро приняли, залатали недостатки 11-го, можно назвать 2.1
-С++17 — модули, сеть, filesystem, аспекты, что там еще получится — попахивает 3.0?
Нет,
-С++98 — «мажорная» версия, первый стандарт, вроде как 1.0
-С++03 — апдейт, фиксы, вроде 1.1
— а с выходом С++11 перешли на модель релизов Google Chrome
Увы, тоже не совсем точное решение. Про автообновление стандарта везде и всюду каждые 6 недель пока можно только мечтать :D
Ну а если серьезно, Вы не считаете, что набор запланированных изменений в 14 был куда меньше? Просто мне кажется, обрадовались, что 14 релиз удалось «быстро выпустить», в срок, и теперь опять раскатали губу на много всего. Да даже 1 или две фичи из планируемого в 17 — будет в 10 раз круче 14 апдейта, как мне кажется (одни модули чего стоят!).

p.s. я знаю про декларируемую схему работы, на хабре даже картинку постили.
По такому поводу хочется включить, всем известную, песню группы «Сектор Газа» :)
Вы посвятили С++ большую часть жизни...
Вопрос вы выделен.
Очень символично, что сегодня вышла первая версия компилятора D написанная на самом D.
Через 15 лет после появления языка. А что еще, кстати, на нем написано за эти 15 лет? Очень символично, да.
Страуструп — легенда.
Книжку его прочитал от корки до корки.
На экзамене преподавателю объяснял разницу типов связей между классами и объектами.

И хотя сейчас на нем не пишу, но желаю долгих лет языку и его создателю.
Жалко, что не было вопросов о его отношении к «убийцам», типа раста. Считает ли он, что они его вскоре заменят, или что плюсам нет конкуренкции, и все эти расты — игры в песочнице… Было бы интересно узнать мнение такого человека по подобному вопросу.
А на расте можно писать библиотеки для всех остальных языков?
И сам отвечу на свой вопрос, да вроде как можно вызывать раст функции из с кода. https://doc.rust-lang.org/book/ffi.html#calling-rust-code-from-c

Так что потенциал есть, но как-то мало документации про это.
Ну для языка с полугодовой историей это вряд ли удивительно.
Не увидел в обещаниях нормальных utf8- строк. До сих пор считается, что строки не нужны?
Такие UTF-8-строки — на самом деле набор байтов:
The type of a u8"..." string literal is const char[].
А нужен бы стандартный способ работы с массивами нормализованных символов как неделимых логических сущностей — в частности со встроенной нормализацией (простым использованием UTF-32 это автоматически не решается, т. к. один символ может быть образован более чем одним code point).
Любые строки — набор байтов. Как их интрпретировать — дело библиотек и пользовательского кода
> А нужен бы стандартный способ работы с массивами нормализованных символов как неделимых логических сущностей
А зачем?
Ну вот зря минусуете. На rsdn, помнится, целый тред был, вопрос-то не праздный. Что делать-то с этим символом? Замена подстроки работает и на utf-8, считать кол-во букв смысла не имеет, замерять длину строки тоже надо не так. Заменять отдельно букву — зачем?
Мне до сих пор интересно, зачем всё-таки надо получать какой-то там энный символ.
А когда это действительно надо (пройтись по символам), тогда можно использовать отдельный алгоритм, но нет смысла хранить так строку по умолчанию.
Ну именно __массив__ может и не нужен, но __нормализованных символов__ нужно для любой мало-мальской обработки текста (см. NLP)
Можно поподробнее, мне реально интересно. Нормализовать и бегать специальным итератором прямо по utf8 строке не решит проблему?
Ну, в целом, с UTF8 совсем не удобно работать, обычно все же используют 2хбайтовый формат. Но за исключеним этого — в целом — навреное решит. Надо только запилить «нормализовать» и «специальным итератором» в «стандартный способ» — и все будет ОК.
Если вы действительно не понимаете, то попробуйте представить, что произойдёт при попытке извлечения символьной подстроки, расположенной далеко от начала строки, из UTF-8-строки, представленной не как набор символов, а как набор байтов, и как это связано с быстродействием.
Я не понял задачу. Что значит «попытка извлечения символьной подстроки»?
Хм, что такое подстрока? Например, «мир» в строке «Здравствуй, мир».
А что значит «извлечь» её? Чтобы её найти, не надо делать ничего особенного, обычный поиск подстроки, в чём сложность?
Получить подстроку такой-то длины (в символах) начиная с символа с таким-то номером от начала строки. Например, в JavaScript для этого служит функция substr().
А откуда берётся номер, с которого надо брать подстроку, и длина подстроки?
Просто если был какой-то поиск для этого, то проблемы опять не будет. А какие ещё могут быть ситуации?
Работая с текстом, удобнее и проще работать с ним как с набором символов, а не байтов, и переход на соответствующий уровень абстракции (преобразовать байты в массив символов) в общем случае имеет смысл осуществить единожды, а не делать это (разбирать всю UTF-8-строку с самого начала) снова и снова при каждой операции.

Это актуально в частности при разборе кода, синтаксические единицы (например, имена переменных или даже функций) которого могут содержать символы вне ANSI-диапазона, а также при анализе текстов на естественных языках — например, для вычисления соотношения количества слов на разных языках, чтобы определить основной язык документа. Не говоря уж о задачах поиска типа «все слова длиной от 1 до 3 букв», «все слова в верхнем регистре» или «определить общее количество символов в тексте».

Безусловно, многие задачи можно относительно успешно решить на уровне байтов, просто это не всегда удобно и целесообразно в том числе с точки зрения быстродействия.
Это сторонняя библиотека.
Если вы о codepoint'ах, то для этого можно взять u32string (basic_string<char32_t>). А если речь о буквах, то там всё сложнее, насколько я понимаю. Например, æ — один codepoint, но одна или две буквы в зависимости от языка.
Я это знаю, как это соотносится с моим комментарием?
Разбор кода, по-моему, везде орудует именно codepoint'ами без нормализаций (ну где надо, чтоб переменная «май» с разными «й» определялась одинаковой?). Даже там, где надо сравнивать «май» с разными «й» можно часто обойтись нормализацией, и работа всё равно идёт на уровне codepoint'ов.
Места, где надо уметь понимать, что «mæ» — это 2 или 3 буквы с зависимости от языка, по-моему, всё же на так часты.

Кстати, есть ли буквы, которые в принципе не умещаются в один codepoint (после нормализации)?
Возможно, вы путаете самостоятельный символ со шрифтовой лигатурой (например, для последовательности букв «fi» в шрифте PT Serif Caption предусмотрен отдельный графический знак, но буквы всё равно две, и каждую можно выделить по отдельности). У строки нет такого атрибута, как язык. Например, в JavaScript свойство length строки æ будет равно 1, а "æ".substr(0, 1) вернёт символ æ, а не его половину. ;-)

В C++ хотелось бы такой же абстрагированной работы со строками, как в JavaScript, на уровне стандартной библиотеки языка. Не критично, не обязательно, не проблема, но было бы неплохо. ;-)

C++ вообще несколько удивляет отсутствием некоторых общеупотребительных функций, привычных по другим, более высокоуровневым, языкам — например, trim(), split(), join(), в результате чего, чтобы просто начать писать программы, требуется сначала реализовать собственную библиотеку таких функций или использовать стороннюю библиотеку. Опять же, не критично, но странновато.
Например, в JavaScript свойство length строки æ будет равно 1, а "æ".substr(0, 1) вернёт символ æ, а не его половину. ;-)

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

C++ вообще несколько удивляет отсутствием некоторых общеупотребительных функций

Это да, есть такое.
Похоже, вы склонны игнорировать некоторые части текста, на который отвечаете. %)
У строки нет такого атрибута, как язык.
Вот именно, но это же вы просили
Не говоря уж о задачах поиска типа «все слова длиной от 1 до 3 букв», «все слова в верхнем регистре» или «определить общее количество символов в тексте».

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

Т.е. когда мы начинаем считать не кодпойнты, а логические буквы, нам надо заранее что-то куда-то преобразовать, зная при этом язык, а потом работать уже с кодпойнтами, но с кодпойнтами-то работать в C++ можно. Вот то, что стандартная библиотека бедна на всякого рода нормализации и trim/join — это да.
Предлагаю остановиться на том, что было бы неплохо иметь в C++ способ работы хотя бы с codepoint’ами как самостоятельными сущностями без необходимости раздувания байтового объёма строк в несколько раз путём использования кодировки UTF-32 и работы с последовательностями по 4 байта вместо реальных codepoint’ов. Или, возможно, мне что-то неизвестно о работе с UTF-32 в C++.
В том, что придется пройти от начала строки. Никакого Боера-Мура, например. Но то же самое и в utf-16, только там переменной длины цепочки из двух-, а не однобайтных элементов.
Я понимаю, о чем речь, но я не понимаю, когда это может быть нужно, комментарий выше с вопросом.
Sign up to leave a comment.

Articles