Pull to refresh

Comments 177

это ведь не случайно, что у вас написано кириллицей? Импортозамещение и все дела?
Ну, C# позволяет писать идентификаторы любыми символами, которые в стандарте unicode определены как Letter, почему бы в C++ не принять эту фичу.
Тут возникает ряд существенных граблей.
Например, обязан ли язык различать Á (U+00C1) и Á (U+0041 U+0301)? Python различает, но приведение всех идентификаторов к NKFC может быть дороговато (в компилятор втягивать что-то размера ICU, причём на этапе, где лексер и так отрабатывает много специфики языка — см. например Clang — где есть лексический парсинг обычного кода, строк препроцессора, кода внутри блока `#if 0`, и т.п.) А вот Go ничего не делает с этим — и можно получить злобные проблемы от внешне одинаковых идентификаторов в разных местах, так, что редактор этого не покажет.
А когда апдейтится стандарт Unicode — менять таблицы вслед?
Требовать ли от внешних средств типа линкера поддержки таких идентификаторов, и что делать, если не поддерживает?

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

Ведь даже наличие ASCII, считаем, стало обязательным только с C++17 — я имею в виду отказ от триграфов, которые применялись для возможности написания на C++ в странных местах типа zSeries с локализованными вариантами EBCDIC…
В случае с C++ всё просто: ничего никуда не конвертируется. В стандарте просто сказано: An identifier is an arbitrarily long sequence of letters and digits. Each universal-character-name in an identifier shall designate a character whose encoding in ISO 10646 falls into one of the ranges specified in E.1.

И компиляторы, хотя и не все, давно позволяют называет идентификаторы по русски. Это вопрос к программистам, а не к стандарту сегодня, на самом деле.
Чувствуется влияние Golang и Rust.
Только не включайте больше ваш сарказм, пожалуйста. А просто перечитайте про инициализаторы в if и switch.
Возможность «инициализатор в if и switch» — это как раз если не впервые в таком виде появилось в Go, то по крайней мере там стало хорошо известно — и доказало свою полезность.
Или Вы знаете другой язык из популярных, где это было раньше?
Инициализаторы в if в C++ были очень давно. Не хватало возможности определить условие, отличное от приведения проинициализированной переменной к bool.
Ну то есть они были практически бесполезны.
А вот именно в такой удобной форме (и с блоком на весь if/switch) — образцом послужил Go.
С указателями получалось очень даже красиво:
if (foo* p = findFoo(id)) { p->bar(); }

Вообще-то образцов послужил C++98, в котором это было разрешено делать в цикле for.

Когда эта фича в C++ появилась — ни rust'а, ни go даже в проекте не было.
Упускаете главный момент.
Новая форма аналогична созданию блока, в котором определены новые переменные, а после этого вложен собственно if. Пример автора статьи:

if (auto it = m.find(key); it != m.end())
{
  ....
}


аналогичен

{
  auto if = m.find(key);
  if (it != m.end())
  {
    ....
  }
  // кстати, тут может быть else-ветка, или даже цепочка else if ... else,
  // в которой эта переменная будет видна
}


но блок не выписывается явно.
По выходу из блока сработают деструкторы; кроме того, новая переменная не будет видна после завершения if — чтобы случайно её не применить где не следует.

Потому — это чисто «сахар». Но очень практически полезный, раз ввели.

Кстати, насколько я понял final draft, несколько отдельных init-statement ввести нельзя. Немного жаль.
Я с этим и не спорю. С моей точки зрения это никак не связано с go, все совпадения с go случайны :).
А Вы где-то ещё видели именно такой же синтаксис — две части через точку с запятой, первая необязательна, но должна что-то инициализировать? Я — только в одном источнике, и его тут уже назвали.
Был бы другой образец — взяли бы его, потому что ничто не мешало, например, создать отдельное ключевое слово и блок за ним (что было бы как-то более понятно при отсутствии такого образца), или сделать другое построение блока (хм, а почему if и switch, но не while?), перетащить GCC вариант ({...}) с уточнением блочного контекста, и т.п.
ну, например, тут:
for(int i=f(x); i<10;) {;;}

распространение этого синтаксиса и на if является вполне логичным продолжением даже без влияния go. Тот факт, что что-то в новом C++ похоже на go ещё не означает, что они двигаются в одном направлении и даже не значит, что именно оттуда оно заимствуется:
Цитирую пропозал:
There are three statements in C++, if, for and while, which are all variations on a theme. We propose to make the picture more complete by adding a new form of if statement.
> распространение этого синтаксиса и на if является вполне логичным продолжением даже без влияния go.

Для меня тут колоссальной разницей является то, что в for три части присутствуют обязательно (даже если пустые), а в if первая может отсутствовать. Именно конкретный метод решения был взят в конкретном месте. Альтернатива, в виде названного в том же пропозале with(), присутствовала в других местах.

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

> There are three statements in C++, if, for and while, which are all variations on a theme.

И он тут же исключает из рассмотрения else для if… мне эта логика ой не кажется корректной.

К слову, else для for и while (по образцу Питона) иногда тоже очень полезно. :)
А Вы где-то ещё видели именно такой же синтаксис — две части через точку с запятой,
А какое это имеет отношение к делу? После не значит в следствии. Мысль о том, что if не хватает инициализации посещала меня ещё до появления golang. Да и сами авторы пропозала тоже мотивируют внутренними потребностями языка.
Вот от этого фрагмента прям какой-то теплой ламповой сишечкой повеяло:
auto res1 = std::to_chars(arr, arr + 128, 3.14f);
Может имело бы смысл его записать в духе современного C++?
auto res1 = std::to_chars(std::begin(arr), std::end(arr), 3.14f);
Как-то понадежнее будет.
Да, согласен, спасибо. Подправил в статье.
Очень странно что to_chars возвражает структуру, а не std::pair
Тогда бы и тут можно было писать что-то типа
auto [ptr, error] = std::to_chars(...)
Так написать можно. Structured bindings, как было сказано в статье, умеют производить декомпозицию типов, содержащих только нестатические открытые члены.

Меня больше бомбануло от


struct to_chars_result
{
  char* ptr;
  std::errc ec;
};

На вход итераторы, а на выход — поинтер… Слов нет, одни выражения. А, ну и голый эррор код. С++ так и не смог нормальную единую обработку ошибок, сплошные костыли.

На вход to_chars подаются тоже указатели. А вообще, to_chars была так спроектированна именно для максимально быстрого преобразования.

Тогда могли бы через string_view. И возвращать тоже часть string_view. Увы, много рассказывают про "безопасные практики" — при этом периодически добавляя способов отстрелить себе ногу.

Ну, вообще-то, to_chars получает на вход char*. Что получил, то и вернул.

Другое дело, что к to_chars_result::ptr можно получить доступ даже если в to_chars_result::ec находится код ошибки… Вот это не есть хорошо. Какой-нибудь std::variant<char*, std::errc> был бы, наверное, уместнее. Но, вероятно, с точки зрения эффективности to_chars_result обходится дешевле.
Вот тут меня тож переклинило. Зачем спрашивается std::optional?
std::optional не поможет вернуть код ошибки в случае неудачи. А вот аналог Rust-овского Result-а в стандартной библиотеке бы не помешал.
UFO just landed and posted this here
Когда уже откажутся от header-файлов?
Когда завезут модульную систему + лет 10 (а может и больше) на избавление от легаси.

А что в них настолько плохого, что от них нужно обязательно избавиться?


PS Интересно, а что мешает писать их не используя?

Время компиляции в них плохое. При каждом include парсятся многомегабайтные заголовки библиотек. А как вы предлагаете их не использовать? Везде писать extern?

Многомегабайтные заголовки сами по себе не такая большая беда. Компиляторы довольно давно научились это дело кешировать. Две гораздо большие проблемы — война макросов и свалка определений.


Война макросов, как можно догадаться — конфликт и наложение эффектов от макросов, определённых в разных заголовках.


Свалка определений — тащим одну маленькую шаблонную функцию, а получаем в область видимости пол-буста вместе с MPL. К тому же, приводит к необходимости тащить вместе с библиотекой заголовки всех её зависимостей (и правильно их раскладывать по местам), которые отметились в заголовках самой библиотеки. И транзитивные в том числе.

Я просто перешел на C#.

Да, в C# с первых версий завезена RAII, просто он требует большего числа ручных действий.

using никак не сравнится по гибкости и удобству с RAII в С++
using это просто синтаксический сахар над finally { .Dispose(); } и ничего более.
Да, я в курсе. Был бы RAII также любим если бы нужно было писать obj.~Object(); каждый раз?)
То есть, разрушение C++ объектов при выходе из скоупа — тоже сахар?
Точно так же можно сказать: «да, в С (без плюсов) первых версий завезена RAII, просто он требует большего числа ручных действий».
В ассемблер завезена RAII, просто чуть больше ручных действий.
UFO just landed and posted this here

А насколько он сравним с С++ по эффективности генерируемого кода?

UFO just landed and posted this here
Все компиляторы C++ умеют компилировать под имеющуюся известную процессорную архитектуру/поколение
В дистрибутивах, где всё собирается из исходников под свою машину, традиционные компиляторы всегда лучше jit.
UFO just landed and posted this here
UFO just landed and posted this here
Но это полумера. Всегда есть граница между «горячими» и «обычными» фунциями. Причём, если разбиение проводить вручную, могут быть и ошибки, и слишком завышенные критерии горячих функций. Некоторые функции, близкие к границе, уже не попадут в «горячие», как могли бы при полной компиляции под целевую архитектуру или при jit-компиляции.
Некоторые функции, близкие к границе, уже не попадут в «горячие», как могли бы при полной компиляции под целевую архитектуру или при jit-компиляции.
Понятно, что сборку строго под конкретную платформу вы не победите.

С JIT'ом же всё не так однозначно: да, он лучше отбирает функции, чем рачная подстройка — но и ресурсов он на себя отбирает больше! Не факт, что в результате выигрыш получится.

То есть создать бенчмарк под любой JIT, чтобы показать его «крутизну» — не проблема. А вот в реальных приложениях… не факт, ой как не факт…
А в реальных приложениях — обычно увы и ах.
Когда уже откажутся от header-файлов?

Когда завезут модульную систему

Свалка определений — тащим одну маленькую шаблонную функцию, а получаем в область видимости пол-буста вместе с MPL.


Я просто перешел на C#.

А я — никуда уходил с Delphi, там всего этого 'счастья' не было изначально.

Поддерживаю, в современных Delphi анонимные функции и обобщенные типы (generics) намного прятнее всех этих templates и deduction rules.

даже если спецификатор constexpr не указан, лямбда все равно будет constexpr, если это возможно


Зачем же тогда указывать constexpr? Явная декларация о намерениях? Мне кажется, С++ движется в этом смысле в сторону питона — explicit is better than implicit.
Постоянное дежавю с питоном. Похоже, в комитет проникли питонисты

auto[iter, ok] = mySet.insert(42);
Python: a,b=1,2
UFO just landed and posted this here
Структура в Python это namedtuple — очень даже разложится.
Ну вообще-то концепция, по которой у функции всегда предполагалось только одно значение-результат, выглядит устаревшей. Она была хороша в период ранней «математизации» понятия функции (особенно по сравнению с subroutine в Fortran, где функции обязаны были быть «чистыми»), но сейчас нет смысла добровольно вжиматься в прокрустово ложе. Тем более что примеров, когда реально передаётся несколько значений, но все кроме одного идут косвенными каналами — полно в любом системном API.

А с чем сравнивать возврат нескольких значений — с Python, Go, Swift, Erlang, Haskell, чем-то ещё — вопрос персонального опыта. В данном случае второй ok это ближе к тому, что я видел по Go. Был бы он первым — был бы стиль Erlang :)
Чем это принципиально отличается от ссылочных out-параметров?
Чтобы ответить на этот вопрос, мне нужно знать уровень Вашей «принципиальности» подхода к этой разнице. Но есть два основных аспекта:
1. Формальный — что это именно части полного результата функции и соответственно не требуют рисования промежуточных переменных.
2. Практический — что в точно такой же манере, как современные ABI предпочитают передавать K первых параметров через регистры, чтобы гонять через RAM — результаты тоже можно передавать через регистры и не заниматься косвенным доступом.
Ну а насколько Вам это будет принципиально — не могу предсказать.
UFO just landed and posted this here
На мой взгляд, минус constexpr в том, что он все равно не гарантирует выполнения на этапе компиляции. Вполне можно было вообще все функции считать constexpr по-умолчанию, а то даже в С++17 много функций из STL не constexpr (в смысле, просто перед ними не приписан constexpr), и их не получается использовать на этапе компиляции.
UFO just landed and posted this here
Вполне можно было вообще все функции считать constexpr по-умолчанию
Нельзя. По историческим причинам. Функции не описанные как constexpr компилятор, в большинстве случаев, в нужном контексте просто не видит (раздельная компиляция, всё такое). Так что constexpr таки нужен.

Но можно было бы всё inline-функции сделать constexpr — это правда…
Нельзя. По историческим причинам. Функции не описанные как constexpr компилятор, в большинстве случаев, в нужном контексте просто не видит (раздельная компиляция, всё такое).

Не могли бы вы объяснить по-подробнее? Почему прям нельзя?
Допустим, не видит компилятор тела функции из библиотеки — ну и ладно, в рантайме вызовем. В конце концов, constexpr не гарантирует выполнения на этапе компиляции.

Другое дело, что время компиляции от этого, скорее всего, сильно выросло бы.
В конце концов, constexpr не гарантирует выполнения на этапе компиляции.
В случае с описанием переменной (а также использования в качестве параметра типа, размера массива и в других местах) — гарантирует.

Не могли бы вы объяснить по-подробнее? Почему прям нельзя?
Потому что описать constexpr-функцию без тела — нельзя, а без constexpr-можно.

Но реально, конечно, это всё поблажки разработчикам компиляторов: constexpr-функции ведь приходится интерпретировать — а для этого, фактически, отдельный транслятор нужен… Вот и ограничивают их. Вначале ограничения были совсем драконовскими, но сейчас потихоньку гайки отпускают.
> Зачем же тогда указывать constexpr?

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

Ну так и нафига козе баян — в смысле нафига явно тут указывать, что функция constexpr? Если возможность её использовать всё равно зависит от конкретного значения?

P.S. Кстати за счёт constexpr любой коспилятор C++ — теперь где-то ещё и инетерпретатор. clang отрабатывает раз в 8 быстрее, чем gcc…

Отличная статья! Всё собрано в одном месте.

Здорово что они дополняют std простыми и полезными функциями. Но вот…
Свертка параметров шаблона (Fold expressions)
Сложность понимания кода все еще очень высока. Все еще нужно разбираться в нюансах, а не читать «на лету» код
Fold expressions, как раз уменьшают сложность работы с variadic templates, ведь код получается куда нагляднее чем с использованием рекурсии.
C++ — это write only язык by design, и то, что вы его могли раньше читать на лету — это была недоработка, которую постепенно исправляют :)
UFO just landed and posted this here

Переменные it и ok живут в меньшем скопе

Это больше синтаксический сахар — не нужна лишняя строка. И да, выше указали более существенное преимущество про сужение scope
for (const auto &[key, value] : myMap)

Наконец-то не надо писать iter->first, iter->second!
Интересно. А настанет ли момент, когда из языка будут удалять ненужные элементы, а не добавлять новые?
Начнется вой о совместимости со старым кодом.

Если так поступить, то C++ постигнет судьба Delphi,
который загнулся после выпуска новой версии не совместимой со старыми.

Верно! Для повторного использования кода вынужден писать на Delphi-7.
Во-первых не загнулся. Во-вторых совместимость вверх у него одна из самых лучших вообще существующих.

Просто не используйте ненужные элементы.

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

Потому всё это напоминает мне PL/1, когда обычный программист не знал большей части языка, из-за чего возникали реальные проблемы с пониманием чужого кода написанного человеком, знающим другие части языка.

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

Очень удобная позиция, но к сожалению не работает.
Когда случайно узнал — радовался неделю. Теперь жду когда же удалят iostream и все что с этим связано. Ну и половину нововведений со времен C++11.

А с iostream-то что не так? Переделать его, возможно, и правда следует. Но удалять?..

У меня стойкое чувство, что iostream (и друзья) спроектированы по двум причинам: оправдать наличие в языке виртуальных классов (ромб смерти) и показать как здорово можно переопределять операторы (в данном случае << и >>). А для того чтобы показать на примере специализацию шаблонов специализирован класс std::vector. И т.д. и т.п.

Эти классы взялись не на пустом месте, а были сделаны как абстракция понятия "поток данных". Во всех современных языках есть свои аналогичные абстракции:


Delphi 7 — TStream
Java — java.io.InputStream, java.io.OutputStream
C# — System.IO.Stream
Python — io.IOBase
Node.js — модуль stream


Поэтому без замены iostream на что-то более красивое убирать его из стандартной библиотеки нельзя.

Полностью согласен. Посмотрите на грамотные java.io.InputStream, java.io.OutputStream. А вот зачем было ввод и вывод мандить в std::ios? И зачем были нужны операторы << и >>. В реальных проектах они неудобны, сложно настраивать форматирование, невозможно делать локализацию и т.д. printf по сравнению с ними просто сказка.
и т.д. printf по сравнению с ними просто сказка.
Ровно до тех пор, пока не придется использовать printf в обобщенном коде. Или до тех пор, пока не придется использовать printf для вывода в определенный пользователем поток данных.

Для тех, кто исходит на известную субстанцию от iostreams, уже давно есть fmtlib. Которая, среди прочего, позволяет работать с пользовательскими типами, для которых определены операторы сдвига именно в std::ostream.

Я думаю, имеется ввиду выкинуть iostreams на мороз именно в текущем виде. Просто ради примера, в Rust абстракции байтовых потоков ввода-вывода требуют реализации по одной функции на чтение и запись, соответственно. Это в самой базовой форме. В С++ тщательно ковыряться с буфером потока и, возможно, самим классом потока.

а конкретней, какие именно новые фичи нужно удалить?
Ну списка я не веду, дабы не расстраиваться. Для примера Вам новая инициализация через { и }.
А чем она хуже того, что было ранее, кроме того что нужно сесть и разобраться в том, как она работает? C++ — expert friendly. Одна из идей языка — не жертвовать гибкостью в пользу простоты изучения. Есть языки с противоположной парадигмой — golang, например.
Конечно, в С++ есть места, где сложность получается избыточной и не несёт полезной нагрузки — с ними пытаются бороться.
Я не говорю, что golang — хуже, просто это другой язык с другой установкой, идеей и целями. Не нужно все языки под golang причёсывать.
Насчет { } немного долгое объяснение. Когда-то очень давно, когда в C++ вводились классы их синтаксически приравняли к структурам. Но в моей реальной практике (и не только в моей) слово class используют, когда нужен полноценный объект, а слово struct, когда нужны только переменные члены класса (и они все открытые). В struct практически не пишутся функции, иногда пишется конструктор, еще реже деструктор. И вот именно дефолтного конструктора по всем членам структуры мне всегда не хватало. Скобки { }
решают это проблему, но заодно вводят путаницу в вызовы конструкторов. По мне, так правильней было бы разделить назначение struct и class. И как минимум сделать дефолтный конструктор для struct по списку членов.
Ну то есть по Вашему мнению тот факт, что {} инициализация могла бы быть сделана ещё лучше означает, что её нужно совсем удалить? Да и Ваш вариант обладает недостатком нарушения совместимости с C++98, так что его превосходство крайне спорно. Новых еще лучших несовместимых языков хватает — D, Rust, golang,… У C++ другие цели, при этом преимуществ более новых языков никто не отрицает.
Два крайних подхода. 1. Если что-то можно сделать в языке без ввода новых конструкций, то новую конструкцию не вводят. 2. Вводить новую конструкцию по любому мелочному поводу.
Истина где-то посередине. Но середина у всех разная. У меня она ближе к пункту 1.
Про разделения struct и class я говорил (увы не уточнил) в контексте, как надо было сделать, когда вводились классы. Сейчас, понятно, это сделать уже нельзя.
Особенно понравился [[maybe_unused]]! Интересно, а скоро будет [[maybe_wrong]]?
Думаю будет полезно определение [[maybe_error]] — если функция не используется, то может содержать ошибки (не компилироваться). Извините за черный юмор.
Извините за черный юмор

Увы! От «плюсов» иногда появляются черные мысли…
[[maybe_unused]] полезен при условной компиляции.
Совершенно неуместная ирония. Вы хорошо знакомы с C++? Встречали паттерн (void)param2;? Это ему более удобная замена. Кроме условной компиляции такое бывает необходимо при перегрузке виртуальных функций, мб ещё где…
Да. Увы. Знаком. Перманентно перевожу с С++ на Delphi-7 (а еще CUDA — там проще без Delphi), всякие паттерны дополнительно затрудняют перевод. С++ может и великий, но не единственный язык, поэтому ИМХО стоит больше думать о сосуществовании с другими языками.
И как это связанно с [[maybe_unused]]? Что в нём плохого? Это же просто подсказка программисту от компилятора типа тех которые выдает статический анализатор.
C++ и так сделал всё необходимое для сосуществования — обертки над C++ доступны практически во всех языках.
Я не говорил, что в [[maybe_unused]] что-то плохое. И уж точно не собирался устраивать холивар «какой язык лучше», я только хотел сказать, что ИМХО слишком много стало архитектурных излишеств, как было сказано выше:

Скоро найти человека, который знает весь синтаксис С++ будет нереально. И это меня печалит.
А почему именно эта конструкция излишняя? При чем тут архитектура? что значит «излишняя» для вас? Так-то до ассемблера можно дойти. И даже дальше.
На мой взгляд, эта конструкция одна из наиболее выразительных, где избыточность выражена через maybe. Архитектура тут при том, что было постановление.

что значит «излишняя» для вас?

Думаю, очевидно, что для меня как переводчика это лишняя заморочка.

Так-то до ассемблера можно дойти.


А что Вы имеете против ассемблера?

Вспомнил, нпр., такое утверждение:

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

Далее:
И даже дальше.

А куда дальше ассемблера?

Какое это имеет отношение к вопросу «почему именно эта конструкция излишняя?».
конструкция одна из наиболее выразительных, где избыточность выражена через maybe
то есть она все таки не излишняя?
А настанет ли момент, когда из языка будут удалять ненужные элементы

Особенно понравился [[maybe_unused]]!
… слишком много стало архитектурных излишеств

то есть, очевидно, слово «понравился» имело ироничный характер.
для меня как переводчика это лишняя заморочка.
Во-первых даже для «переводчиков» эта заморочка совершенно не лишняя т.к. показывает намерение о том, что эта переменная на самом деле, вероятно, не нужна и её можно не переносить. Во-вторых, мне кажется, очевидно, что интересы переводчиков с С++ при разработке стандарта имеют крайне низкий приоритет. Гораздо важнее удобство C++ разработки.
А что Вы имеете против ассемблера?
Вы специально из контекста выдёргиваете? Там речь шла о том, что ассемблер — плохой C++ (и наоборот, но речь не об этом). Соответсвенно, «излишества» — понятие относительное. И снова возвращаемся к вопросу, почему Вы считаете, что конкретно "[[maybe_unused]]" — излишество, причём самое явное в новом стандарте.

А приводить в качестве аргумента об архитектуре ЯП постановление ЦК КПСС по архитектуре зданий от 1955 года — это вообще какая-то самодискредитация. Во-первых не та предметная область. Во-вторых в строительстве нет единого понимания о том, что такое хорошо, но постановления ЦК КПСС тут точно не авторитет, также как и по отношению к генетике и кибернетике. И в третьих последствия этого решения всем нам печально известны в виде страшных как смертный грех жилых районов в городах СНГ, которые не критиковал только ленивый. Примеров того, как от этой «архитектуры» старались и стараются избавиться, везде, где представляется такая возможность — валом.
А приводить в качестве аргумента об архитектуре ЯП постановление ЦК КПСС по архитектуре зданий от 1955 года


Я сказал "архитектура", а не «архитектура ЯП»! И не приводил в качестве аргумента, а только в качестве сравнения, метафоры.

очевидно, что интересы переводчиков с С++ при разработке стандарта имеют крайне низкий приоритет.


Именно это я и говорил:

стоит больше думать о сосуществовании с другими языками


И возможно ли доказать, что удобство C++ разработки повысилось?:

Гораздо важнее удобство C++ разработки.
Так два года назад начали:
«Within C++ is a smaller, simpler, safer language struggling to get out.» — Bjarne Stroustrup
«Within C++ is a smaller, simpler, safer language losing struggle to get out.»

Fixed

Ещё рано судить, мне кажется. Я готов дать Core Guidelines шанс.

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

С++ несовместим ни с кем кроме С++

C++ совместим (хотя бы частично) с С, а это в эмбеддеде уже огромный плюс. Вот кто точно почти не совместим ни с кем, кроме себя — это стандартный .NET, который представляет собой вещь в себе и приносящий дикие боли при попытке подружить что-либо с нативной библиотекой (писать кучу [DllImport] та еще романтика, буэ)

Для сложных случаев в .NET существуют аж две альтернативы DllImport — COM и C++/CLI

И обе работают только в пределах MS Windows?


Но дотнет еще ничего, я боюсь подумать про Node.js и прочие "новомодные" фреймворки.

Да нет, вроде бы и в линуксе есть… Хотя сам я не проверял как оно там работает.
Но дотнет еще ничего, я боюсь подумать про Node.js и прочие «новомодные» фреймворки.

Обёртки писать, как ещё. Для Python много всего понаписано. Для node.js тоже статьи попадались — видимо как-то можно.

Эти функции были добавлены в C++ для совместимости с C, где они появились в C99.

Интересно, есть ли утилита или файл конфигурации для линтера, чтобы обеспечить строгое следование этим гайдам?

Насчет параллельных алгоритмов — один из участников CppCon активно критиковал то, что этот функционал слишком легко доступен для новичков. Надо всего лишь добавить один параметр. Такая простота дает новые возможности отстрелить не только ногу :)

By pretending that parallelisation is simple – it has an enormous potential for unsuspecting developer trying to use it – and getting the whole project badly burned

Детали здесь
Так C++ всегда был expert friendly, новичкам ничего не обещали :)
С++ — очень дружелюбный язык. Проблема в том, что он сам выбирает, с кем ему дружить.
Большое спасибо за прекрасный обзор и, главное, ясные примеры! Вы решили последовать совету Страуструпа на CppCon 2017 :) Он сказал, что самое главное — это не фичи, а примеры их правильного применения.

Вообще, новый стандарт никак не минорный, каким был С++14. И хотя многое из списка выглядит как синтаксический сахар — все очень востребовано и поможет заметно улучшить читаемость и не писать лишний код. В который раз убеждаюсь, что в комитете сидят очень адекватные люди. А всеми ожидаемые Networking и Modules задерживают не просто так — там слишком много всего надо учесть, чтобы потом не переделывать и не ломать совместимость.
С одной стороны, много клёвых удобных фич, с другой — все дальше и дальше write-only…
Чёрт возьми, а я и не знал, что столько всего полезного всё-таки попало в стандарт! Декомпозиция через auto, свёртки для variadic templates — это очень прикольно. Надо начинать использовать)
Во что превратился С++? Есть люди не из суперов заседающих в комитете, кто знает все детали языка?
Это сродни жалоб на то, что не все знают русский на уровне профессора фил-фака МГУ, а профессор МГУ не понимает профессиональных жаргонов.
Вообще эта проблема сущесествует уже давно, шаблонная магия неспроста называется магией. К счастью, она обычно изолированна в тех местах, куда большинство разработчиков почти не заглядывает.
Нельзя создать тривиальный для понимания и при этом универсальный (гибкий) язык, коим старается быть C++. К этому идеалу можно стремится, и C++-next + Core Guidelines как раз в этом направлении и двигаются.
универсальный (гибкий) язык


универсальный = гибкий?

Самый универсальный ЯП — это ассемблер: остальные языки на него транслируются и составляют подмножества комбинаций ассемблерных инструкций. При этом не все возможные комбинации задействованы.
Нет такого языка, как «Ассемблер», а есть языки «Ассемблер ARMv8», «Ассемблер x86» последний ещё и с разными синтаксисами (Intel, AT&T) и диалектами (i386, i686, SSE2).

И который из этих ассемблеров универсальный?
Каждый универсальный для своей платформы.
Почему тогда нельзя сказать: каждый язык программирования универсальный… для своего класса задач. Не теряется ли суть понятия «универсальный»?
Есть языки, которые подходят для широкого круга задач — их называют универсальными, а есть для узкого круга.
Так и ассемблер работает на одной платформе, а не на широком круге.
Я не про платформы, а про задачи. Язык запросов SQL, нпр., реализован на многих платформах, но не является универсальным.
Платформу выбирают под задачу. Из требований к задаче вытекает, на какой платформе она будет решаться. Таким образом язык, привязанный к платформе, заведомо проигрывает в универсальности на всём множестве задач.
Платформу выбирают под задачу.
Кто выбирает? Я не выбираю. Сейчас все задачи решаю на Intel Core i7, а было время, когда решал на ЕС-1022. А если серьезно, то существуют эмуляторы (см. вики) — можно любой ассемблер на любой достаточно мощной машине воспроизвести. Универсальность — свойство, заложенное в языке, а не ассортимент реализаций. Нпр., нет Дельфи-7 для CUDA, но возможно реализовать.
Кто выбирает? Я не выбираю
Значит, узкий спектр задач. Например, игру типа Pokemon GO логично делать на мобильных платформах, а не на Core-i7.
А если серьезно, то существуют эмуляторы (см. вики) — можно любой ассемблер на любой достаточно мощной машине воспроизвести
А что толку, если платформа (в более широком смысле, чем аппаратная) вам не позволит писать на ассемблере. Например, если требуется сделать клиентскую логику на веб-странице.
Значит, узкий спектр задач. Например, игру типа Pokemon GO логично делать на мобильных платформах, а не на Core-i7.
ИМХО Pokemon GO очень специфичная задача. Но вроде QEMU поможет сделать ее на Core-i7.
Универсальность — свойство, заложенное в языке, а не ассортимент реализаций
Ассемблер для ЕС-1022 и для Core-i7 — один язык, или разные?
И оба — универсальные?
Я вас понял, особенно после замечания про QEMU. Универсальность вы рассматриваете с математической, а не практической стороны. Ну, как машину Тьюринга.

В таком случае, ассемблеры не являются чем-то особенным, любой полный по Тьюрингу язык можно считать универсальным. Тот же javascript — пишем на нём эмулятор x86, ставим windows, qemu, запускаем эмулятор андроида — задача выполнена.
Согласен. В начале ветки я спросил:

универсальный = гибкий?

Изменю утверждение:

Самый универсальный гибкий ЯП — это ассемблер: остальные языки на него транслируются и составляют подмножества комбинаций ассемблерных инструкций. При этом не все возможные комбинации задействованы.
Я понимаю слово «гибкий» как «адаптирующийся к изменениям».

То есть язык, который с одной стороны развивается в соответствии с веяниями программисткой моды (как c#), с другой стороны, позволяет с минимальными усилиями модифицировать программы под новые требования (как специализированные DSL).

В этом смысле, ассемблеры — наиболее «жёсткие» языки.
Зависит от определения. Я понимаю слово «гибкий» как возможность по-разному решить задачу. Давно не возникало таких задач и не знаю как сейчас, когда компиляторы очень хорошо оптимизируют, но раньше удавалось получать ускорение переписывая критические участки на ассемблере.
Если исходить из того, что на ассемблере можно написать абсолютно всё, то да — он самый универсальный и есть. Другое дело — что он не кроссплатформенный, что тоже верно. Думаю — спор из-за термина 'универсальный'.
Удаленные возможности

Как же они на это пошли? А как же священная обратная совместимость со старым кодом и с C?
Ну так-то удаленные фичи были как минимум со времен C++11.

А можно познакомиться со списком удалённого?


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

А можно познакомиться со списком удалённого?
Приложение С в стандарте этому посвящено.

PS по Вашим словам, похоже практики настолько мало знакомы с нововведениями, что не успевают даже заметить и прочувствовать, что что-то добавили и удалили.
Не совсем так. В C++11 удалили какие-то фичи, которые были обьявлены как «устаревшие» в C++03, в C++17 — то, что было обьявлено устаревшим в C++11.

export template удалили, который ни одним компилятором не поддерживался (хотя вру — вроде один экспериментальный всё же был).
Это вы еще не видели brace инициализаторов в сочетании со структурами
 this->exteriorPortals.push_back({
                        groupId: i,
                        portalIndex : -1,
                        portalVertices: {},
                        frustumPlanes: frustumPlanes,
                        level : 0
                    });

Вот это сейчас совершенно легальный код в С++. Привет JS?
Это не валидный код в C++. Это всго лишь пропозал для C++20, причём в том виде, как вы его описали — это всё равно ошибка.

И да — это весьма полезная фича, благо она есть в C99, активно используется во многих проектах, а в режиме C++ поддерживается, например, clang'ом.

Ну не знаю. Я могу предположить, что это самодеятельность разработчиков компилятора, но как минимум gcc 6 съедает такой код и не давится.


Использование этой конструкции помогло мне очень эффективно портировать код с JS на C++.

Я могу предположить, что это самодеятельность разработчиков компилятора, но как минимум gcc 6 съедает такой код и не давится.
Прекрасно. В таком случае дать ссылку на https://godbolt.org/ вас, разумеется не затруднит. Дополните ваш пример до компилируемого кода — и можно будет что-то обсуждать.
Вот, если я всё правильно понял: godbolt.org/g/ZSLJgN

Если что, не я автор изначального комментария, но мне стало интересно и я проверил.
Ого. Не ожидал. Clang тоже сьедает, кстати — но по крайней мере warning'и выдаёт.

Это GNU'сное расширение, которое в стандарт C99 (а теперь и C++20) решили не вносить. Было принято решение использовать другой синтаксис, никакого отношения к JavaScript'у не имеющий.

То есть в GCC реализовали инициализаторы с одной стороны криво и не полностью, а с другой — со своими собственными расширениями.

Прэлестно, просто прэлестно.
Да, я так же засомневался, что это стандартное поведение, но стандарт изучить не успел. Но так же подумал, что это один из вариантов инициализации через точку:
struct A { int x; int y; int z; };
A a{.y = 2, .x = 1};
A b{.x = 1, .z = 2};
Такие инициализаторы были доступны в GCC еще 20лет назад, а после выхода C99 они считаются deprecate и теперь нужно использовать C99 синтаксис.
С чего вы взяли что они реализованы криво и не полностью?
Если вы не знали что 90-99% новых стандартов C и C++ сначала обкатываются на GCC то это тоже, просто прэлестно. И «как ни странно» рано или поздно высокий процент расширений GCC попадают в стандарт.
Constexpr if

До C++17 нам пришлось бы использовать SFINAE и enable_if:

Не понял, зачем?
template <typename T>
T GetValue(T t) {
    return t;
}

template <typename T>
T GetValue(T *t) {
    return *t;
}

int main()
{
    int i = 5;
    
    std::cout << GetValue(i) << std::endl;
    std::cout << GetValue(&i) << std::endl;
}

В общем случае такой подход не подойдет. Например для std::is_class или std::is_arithmetic.
Sign up to leave a comment.