Pull to refresh

Comments 407

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

Никто не написал предложение о их включении в стандарт. Вы можете стать первым :)

Ну логично чтобы это были те же люди которые пишут компиляторы:) Разработчики, мейнтейнеры. Они кстати участвуют в стандартизации C++ ?

Они кстати участвуют в стандартизации C++ ?
Участвуют, но подавляющее большинство предложений приходит не от них.

У них с расширениями и так всё хорошо: у себя они их использовать могут, а если кому-то это вот всё нужно в стандарте — так пусть он с внесением предложений и бодается.

В основном от них исходит критика, когда что-то из предложений нереально реализовать…

Другими словами: они будут критиковать предложение внести в стандарт расширение от конкурентов.

Тоже случается, но очень редко. Как правило разработчики компиляторов оказываются «по одну сторону баррикад»: реализуемость фич коррелирует скорее со свойсвами фич, чем со свойствами компилятора.
Да, участвуют. Все предложения проходят через них, без их олобрения предложение принято не будет
UFO just landed and posted this here
Ответы (довольно очевидные, в общем):
1. Описание языка в antlr или, тем более, json, принципиально невозможно, так как парсер языка C++ обязан включать в себя интерпретатор достаточно большого подмножества языка C++ (неожиданно, но факт).
2. Какие 3-символьные сокращения вам вдруг потребовались и зачем?
UFO just landed and posted this here
Вопрос конечно больше к Микрософт. Почему нет такого для С++?
А… зачем? Что в с этим собрались делать и как вам наличие описания, которое не позволяет распарсить код поможет?

Эти спеки из чего-то культурно сгенерированы. И полагаю, что не из XML. Хотя возможно.
Ну фиг его знает в каком оно виде существует. Можете взять исходники, если очень нужно, и распарсить. Ориентируясь на nontermdef, terminal и так далее.

Только толку от этого — нуль, так как без интерпретатора C++ вы распарсить ничего не сможете, а если у вас откуда-то взялся интерпретатор C++, то у него всё это уже есть, в том или ином виде.
UFO just landed and posted this here
А можно поподробней про первый пункт?

Если я ничего не путаю, то вот вам пример:


constexpr bool foo(int n) { ... }

template<bool b> struct bar {
    static int value;
}

template <> struct bar<true> {
    template<int n> value { }
}

template <int n> struct baz : bar<foo(n)> { }

baz<42>::value<3> v;

В зависимости от значения, которое вернёт функция foo, последняя строчка может оказаться как объявлением переменной bar<true>::value<42> v, так и выражением (bar<false>::value < 3) > v

Это вы адатировали пример ещё аж с C++98 для C++11 (там не было constexpr функций, но можно было использовать sizeof).

В C++11 с появлением constexpr фукнций всё стало гораздо веселее. Можно много всякого наустраивать такого, что без интерпретирования кода вы это не распарсите…

Главная фишка: грамматика C++ устроена так, что её нельзя распарсить не умея отвечать на вопрос «это имя — это имя типа, шаблона или чего-то там ещё». Отсюда, кстати, необходимость помечать через typename и template зависимые типы и шаблоны внутри шаблонных функций.
ANTLR грамматика в формате JSON? Это вообще как?

text -> string -> JSON с одной строкой: о)

Нет ли планов на будущее добавить в стандарт контейнер для работы с матрицами, наподобие библиотеки numpy в python? А то уже С++20, а двухмерный массив нужно делать или в C-стиле, или vector<vector>.
Зачем? numpy внешняя либа, пусть и в крестах так будет.
Может не совсем аналог numpy, но контейнера для работы с многомерными массивами не хватает. В boost есть ndarray, но boost не всегда можна использовать.
Сейчас идёт работа над включением в стандарт BLAS и LAPACK. Будут вектора и матрицы, со всеми операциями над ними.

Отлично. Хорошо бы сразу предусмотреть выравнивание строк в памяти. Это в частности полезно для работы с текстурами.

Это немного не то. Может быть требование чтобы размер каждой строки был выровнен например по 4 байта.
Для этого есть alignas. Он ещё дольше существует.

Или вы про то, что хорошо было бы, если бы вектора и матрицы такие штукм поддерживали — это да, возможно.

Параллелизм тоже сразу будет в раздаче?

Добавляйте DNN (convolution / grouped-convolution) — это не всегда оптимально через GEMM из BLAS реализовывать.
Если вам чего-то не хватает в существующем предложении — пожалуйста, изложите подробно свои мысли и, желательно, напишите proposal. Так же приложите ссылки на предлагаемые для включения прототипы.
Вот бы glm перенесли, если речь зашла о матрицах
glm поддерживает SIMD оптимизации, это хороший аргумент в сторону glm. Мне нравится что linalg в одном хедаре, хотя настраивается это всего один раз)
Почему все так боятся поломать совместимость со старым кодом? Если авторы не хотят мигрировать, то legacy-код все так же может компилироваться старой версией компилятора.
Но он не будет линковаться с новым кодом. То есть вы будете вынуждены пересобрать legacy-код с новым компилятором, или, если пересобрать нет возможности, вы на всегда останетесь со старой версией стандарта.
В C++17 ещё далеко не все и всё разжевали и прочувствовали.
Так что когда-то АБИ придётся перекраивать, пусть это будет фишкой 23х плюсов — до его внедрения на практике ещё далеко. К тому же изменений не так уж и много в 20м — скорее чистка стандарта, возможно подготовка к «глобальным» изменениям в 23м… ИМХО.
Хм, если Concepts, Coroutines, Modules, Ranges для вас небольшие изменения, то что же является большими?
Ranges — просто библиотека, хоть и классная, могла бы появиться ещё в 11. Поправьте, если ошибаюсь.
Concepts — отлично для шаблонного кода, но не позволяет делаеть ничего нового, чего нельзя было бы раньше. Тоже поправьте, если что.
Модули — поверю, когда увижу, что подключение новых 3rdparty библиотек перестало быть болью в голове и заднице :)
Корутины — прикольно, но специфично, запутанная фича для написания запутанного кода с неочевидным control flow. И, помню, в предыдущих TS недоставало реализации каких-то классов, когда компиляторы уже поддерживали корутины. В С++20 всё нужное будет из коробки?
поверю, когда увижу, что подключение новых 3rdparty библиотек перестало быть болью в голове и заднице

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

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

Ну в моем представлении основная проблема с использованием сторонних библиотек две:


1) Нужна сборка для конкретно твоего компилятора и возможно твоего C++ runtime для того чтобы использовать чужой код, поэтому очень часто нужно собирать из исходников
2) В сторонней библиотеке используется система сборки X, а у тебя Y


И отсюда возникают разные крайности, от библиотек в виде одного заголовочного файла до нежелания вообще использовать любой сторонний код.


Не уверен как тут модули могут помочь. Адепты "однозаголовочных библиотек" не добавляли даже один .cpp файл (ну кроме тестов), не вижу причин чтобы они перешли на модули. А не для однозаголовочных библиотек все также система сборки X vs Y.

Проблема не в системе сборки как таковой, мне не трудно скопом добавить все исходники из /src/ в свою систему сборки. Источник проблем — запутанная, сложная, непонятная конфигурация с кучей макросов и/или условным включением файлов.
Так что да, мой выбор №1 — header-only библиотека, а №2 — отказ от библиотеки с нетривиальной сборкой.
Header-only библиотеки очень сильно замедляют сборку проекта. И precompiled headers не сильно с этим помогают. В последнее время все стали делать хорошие cmake файлы, которые удобно подключать. Вот подключение библиотеки в 3 строчки, cmake сам выкачает и подключит.
FetchContent_Declare(
  Catch2
  GIT_REPOSITORY https://github.com/catchorg/Catch2.git
  GIT_TAG        v2.13.4
)
FetchContent_MakeAvailable(Catch2)
target_link_libraries(${PROJECT_NAME} Catch2)
Разве это не сделает библиотеки более модульными, а значит, удобными в использовании?

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


Но больше преимуществ у модулей нет. Библиотеки все равно будут распространяться в исходных кодах и компилироваться на месте.

UFO just landed and posted this here
Это личный опыт? А какие сборки тестировались? Инкрементальные, полные, unity билды?
UFO just landed and posted this here
Не весь код, а только импортированные символы. Потом для перекомпиляции зависимости можно не полагаться на timestamp source файла, а на хэш бинарного интерфейса модуля, что ещё уменьшит количество перекомпиляций.
Не, тут есть простор для оптимизаций.
Ranges и datetime — добавлили отличные библиотеки для рантайма
Концепты — добавили для метапрограммирования и не только
Модули — ускоряют компиляцию
Корутины — позволяют использлвать нечто абсолютно новое
Constexpr всякое — позволяют compile time вычисления делать с обычным синтаксисом
Новые правила для тривиальных типов — ускорение рантайм кода


Тут каждый аспект языка улучшили. Вы чего-то ещё большего хотели? Скажите хоть чего именно :)
Тут каждый аспект языка улучшили. Вы чего-то ещё большего хотели? Скажите хоть чего именно :)

Например, чтобы когда у клиента падает приложение, а как вы знаете, приложения на С++ при ошибках программирования любят падать, можно было получить хоть какую-то информацию для диагностики. Я о std::stacktrace.

Или, например, сеть. Это было бы действительно принципиальным расширением функциональности языка, как поддержка многопточности в С++11.
Ни в коем случае не хочу преуменьшать нововведения С++20, я очень рад, что мой любимый язык развивается, и последние 10 лет делает это стабильно и по плану, а не как 20 лет до того. Но полностью согласен с автором сообщения, которое мы уже так долго комментируем: С++20 — это больше patch release, чем принципиальные инновации в языке. Ближе всего к инновациям, пожалуй, корутины (к которым я субъективно настроен скептически).
Это было бы действительно принципиальным расширением функциональности языка, как поддержка многопточности в С++11.
Между поддержкой сети есть разница — и она принципиальна: поддержку сети можно добавить сторонней библиотекой, а вот поддержку многопоточности — нет.

Хотя поддержка сети бала бы полезной вещью, спору нет, но мне кажется рановато пока: придётся поддерживать IPv4 и всю связанную с ним муру. Вот в C++23 или C++26 уже можно вносить поддержку, жёстко завязанную на IPv6…

Ближе всего к инновациям, пожалуй, корутины (к которым я субъективно настроен скептически).
Короутины изменят «всё» (в некотором смысле) — так же как метапрограммирование изменило «всё» (в некотором смысле).

Однако произойдёт это скорее ближе к 2030му, чем к 2022му. По одной простой причине: компиляторы пока оптимизируют короутины отвратительно — а раз так, то разработчики будут их избегать. А раз их избегают разработчики — не очень будут напрягаться и компиляторщики…

Тут нужна какая-нибудь популярная библиотека на корутинах (как STL в своё время).
UFO just landed and posted this here
Нет — я реалист. Сегодня поддержка IPv6 — примерно у 30%. Порог 50% — будет где-то через 3-4 года. После пересечения границы 50% можно спокойно перестать в новых проектах IPv4.

Эта простая эвристика, неплохо работающая: примерно так было и с Windows XP и со многим другим.

Наличие отдельных «отстающих» групп (Китай у Windows XP, Россия у IPv4) ничего не меняет: если большинство уже перешло и поддержки больше нет — то это их проюлемы, не ваши.

Плюс, опять-таки многое зависит от API: если API будет достаточно высокоуровневым, то факт существования и использования IPv4 может оказаться скрфтым от приложения.
чтобы прилагать какие-то усилия к переходу нужна мотивация. У крупных хостингов/провайдеров и прочих интернет компаний такая мотивация идет от того, что IPv4 адреса начинают потихоньку заканчиваться и, соответственно, дорожать. А вот у всего среднемелкого, коего всё-таки большинство, такой мотивации нет и в обозримом будущем не появится. И я подозреваю что если какой-нибудь гугл начнет популяризацию, например ухудшит ранжирование сайтов без IPv6, их закидают всяким непотребством
Нужно появление 1 (одного) популярного приложения, разработчиков которого задолбает борьба с IPv4. После чего всё случится очень быстро.

У меня где-то дома валяется стаеренькая книжка Novell, которая описывает как IPX и TCP/IP будут сосуществовать «в ближайшие 10 лет».

Вот только вышла она в 1994м… А до 2004го IPX дожил только в каких-то заповедно-исторических нишах… Ибо появление WWW немедленно сделало IPX устаревшим.

То же самое, скорее всего, случится и с IPv4… после пересечения уровня 50%, скорее всего.
придётся поддерживать IPv4

А его в любом случае придется поддерживать. Он никуда деваться не собирается. Не говоря уже о том, что до полноценного пришествия IPv6 мы наверное не доживем.
Доживём, не бойтесь. Процесс перехода идёт, хоть и с некоторым отставанием от плана.
Только это не изменит того факта, что ipv4 никуда не денется. Банально не нужно ipv6 поднимать везде и всюду. Не только интернетом единым все живет.
Ну да, так же и про IPX говорили четверть века назад. Вот один-в-один те же слова.

IPX убил взрывной рост IT мира. На рубеже веков покупка и обновление магистрального и провайдерского оборудования шла такими темпами, что реально раз в 2-3 года провайдеры меняли у себя все, вплоть до кабелей и секретарш (на последнее тоже были деньги). IPV6 вышел в момент когда большинство провайдеров уже все сделали, соcтавили планы на 10 лет и дрожат над капекc/опекс, поскольку прибыли поужались по сравнению с началом века, а затраты растут. Поэтому IPV6 активно появляется у новичков или совместно с какими-то глобальными модернизациями, типа замены кабелей и оконечного оборудования.

IPV6 вышел в момент когда большинство провайдеров уже все сделали, соcтавили планы на 10 лет и дрожат над капекc/опекс, поскольку прибыли поужались по сравнению с началом века, а затраты растут.
Вот только было это почти 10 лет назад. Если посмотреть назад, то 1% был аж в 2013м году.

У нас в локалке, кстати, IPv4 больше нету. NAT64 есть, правда.

Поэтому IPV6 активно появляется у новичков или совместно с какими-то глобальными модернизациями, типа замены кабелей и оконечного оборудования.
Ну да, конечно. И вы хотите сказать, что в Германии поменяли за последние 10 лет половину оборудования, а в Росии — меньше 5%.

Как ни странно, но в Германии как раз таки идет активная модернизация, ибо сети там отстали от тех же США чудовищно. Основная технология там по-прежнему ADSL. В РФ же из-за позднего старта система в целом довольно современная и срок её устаревания ещё не пришел. Кроме того в РФ сейчас заканчивается фаза поглащений мелких провайдеров. Грубо говоря всем был не до модернизаций.

По одной простой причине: компиляторы пока оптимизируют короутины отвратительно — а раз так, то разработчики будут их избегать.
хехе, решение на отвратительно оптимизированных корутинах всё еще заметно быстрее и на порядки выразительнее async/thread и велосипедов на их основе построенных, так что нет, избегать их точно не будут. Я сам их уже заждался — есть легаси, которое приходится поддерживать и оно так и просится быть переписанным на корутины
хехе, решение на отвратительно оптимизированных корутинах всё еще заметно быстрее и на порядки выразительнее async/thread и велосипедов на их основе построенных
Ну threads — да, это понятно. Но короутины напрашиваются на ranges и вообще любые циклы for… и вот там то, что творят современные компиляторы — это форменное непосредство…

На самом деле, самое интересное — это как раз интегрировать корутины с сетевой библиотекой, чтобы псевдоблокирующая семантика в коде транслировалась в co_yield + неблокирующий ввод/вывод. Жаль, что совсем по-человечески это без дополнительной поддержки со стороны ОС не сделать (кстати, в винде с IOCP это должно имплементироваться проще, чем в юниксах), но даже наполовину по-человечески будет уже огромным прорывом.

А что не так с поддержкой IPv4, и о какой "связанной с ним муре" идёт речь?


Если речь идёт о стандартной библиотеке, а не о реализации TCP/IP (которой в стандартной библиотеке делать нечего!), то вся поддержка IPv4 должна заключаться в парсинге адреса и его передаче ОС, ну и в обратную сторону.

Переменная длина адреса, связанные с этим кучи структур сокетов и прочее.

Если жёстко зафиксировать, что адрес = 128 bit (без вариантов), транспорт — IPv6 (без вариантов) и так далее, то интерфейс заметно упростится.

А внутри либы да — можно спрятать поддержку IPv4 от приложения, тут нет проблем.

А все другие протоколы идут лесом? Даже если вы откажитесь от IPv4 — Fibre Channel и MPLS никуда не денутся. А ведь есть ещё оверлейные сети со своими адресами.


Да что там, банальный AF_UNIX живее всех живых, но влезать в 128 бит наотрез отказывается.

А все другие протоколы идут лесом?
Да. Легко.

Даже если вы откажитесь от IPv4 — Fibre Channel и MPLS никуда не денутся.
MPLS вообще на другом уровне работает, на клиентский API он не влияет никак. Fibre Channel — штука интересная, но совершенно непонятно каким боком касающаяся прикладного кода. Если очень нужно — можно его тоже «вложить» в IPv6, благо трёхбайтные FCID в 128битный IPv6 вкладывается тривиально.

Да что там, банальный AF_UNIX живее всех живых, но влезать в 128 бит наотрез отказывается.
И не нужно: много у нас систем с поддержкой AF_UNIX, но без поддержки IPv6 остались?

Впрочем где-то вы правы: C++ — это не про удобство и минималистичность, это про кучу фич, «чтобы никто не ушёл обиженным».

Так что скорее всего будет очередной дико неэффективный монстр в духе … а жаль.
И не нужно: много у нас систем с поддержкой AF_UNIX, но без поддержки IPv6 остались?

А где тут связь-то? Почему для использования AF_UNIX система должна остаться без поддержки IPv6?

Потому что вряд ли на новую библиотеку будет переписываться что-то старое, что уже работает. Какой смысл?

А если вы разрабатываете что-то новое, то костыль в виде AF_UNIX — вам не особо и нужен.

Но да, это, как бы, не стиль C++… тут скорее в библиотеку запихнут поддержку всего, включая AppleTalk, а потом будут думать как сделать так, чтобы вот это вот всё не сильно тормозило.
Вы наверное что-то путаете. AF_UNIX вам может пригодиться вне зависимости от поддержки IPv6, хотя бы потому, что он производительнее чем использование TCP/IP стека
Ну уж называть AF_UNIX костылем… Имхо, сейчас это очень недооцененная технология. По факту он:
1. Производительнее любого другого стека, так как ни при каких случаях не покидает пределов хоста.
2. Безопаснее, так как к нему невозможно получить доступ извне. Ни прослушать, ни взломать вас через него не смогут.
3. Имеет встроенное разграничение доступа на уровне fs. Мало иметь доступ к системе, чтобы получить доступ к сокету нужно еще и исполнять код от имени пользователя/группы, которым этот доступ разрешен.

Для любого взаимодействия внутри машины AF_UNIX будет предпочтительнее, чем AF_INET6/AF_INET. Это просто протоколы с разными целями.
UFO just landed and posted this here
Concepts — отлично для шаблонного кода, но не позволяет делаеть ничего нового, чего нельзя было бы раньше. Тоже поправьте, если что.
да, можно, но среднему с++ программисту сейчас нужна дока чтобы написать простейший ниблоид.
Корутины — прикольно, но специфично, запутанная фича для написания запутанного кода с неочевидным control flow
киллер фича корутин — их control flow прямолинеен, очевиднее уже попросту некуда
Зачем среднему программисту писать ниблоид? Сколько всякой constexpr фигни на шаблонах понаписывал за последние года три — ни разу с такой задачей не столкнулся. Я, конечно же, не обобщаю свой опыт на всех программистов С++, но проблема кажется очень синтетической. Для чего лично мне нужны концепты — замена уродливых enable_if и более осмысленные сообщения об ошибках.

А проблема ли это? Сейчас в линуксе есть libstdc++.so.6, ну будет libstdc++.so.7 для нового C++, а параллельно останется шестая версия для старого софта со старым ABI. В Windows и подавно есть WinSxS для предоставления нужных версий библиотек каждой программе (если не ошибаюсь). Да, для legacy придётся жить со «старым» стандартом, но на то он и legacy. Не самая страшная ситуация, бывает, нужно вообще виртуалку ставить полноценную, потому что из-за слишком нового ядра и слишком старой libc вообще ничего не запускается, сразу с сегфолтом падает.

А проблема ли это?
Да, проблема.

Сейчас в линуксе есть libstdc++.so.6, ну будет libstdc++.so.7 для нового C++, а параллельно останется шестая версия для старого софта со старым ABI.
Когда libstdc++.so.6 появился (а было это в далёком от нас уже сегодня 2004м году) — расколбас был конкретный. А тогда библиотек было ещё написано куда как меньше, чем сегодня.

Переход на libstdc++.so.7, скорее всего, не случится никогда (точно так же как и libc тоже будет, уже навечно, будет libc.so.6), но даже изменения, случившиеся в 2015м (когда появился _GLIBCXX_USE_CXX11_ABI и всё с ним связанное) тоже были довольно-таки тяжёлыми и в результате многие организации перешли на C++11 вот буквально год-два назад.

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

Да, для legacy придётся жить со «старым» стандартом, но на то он и legacy.
Это вам хорошо рассказывать такие сказки если у вас в проекте только десяток человек и всё в одной организации. А если у вас сотни компонент от десятков вендоров, то переход растягивается на годы.
Когда libstdc++.so.6 появился (а было это в далёком от нас уже сегодня 2004м году) — расколбас был конкретный. А тогда библиотек было ещё написано куда как меньше, чем сегодня.

Так это не повод вообще не делать изменений. Так можно и про systemd рассказать, тоже был расколбас, но ничего, перетащили помаленьку. Речь ведь не о полной поломке совместимости уровня «вчера всё работало, сегодня ничего не работает», а о постепенной миграции. Никто не заставляет компилировать библиотеки самым свежим компилятором, многие до сих пор на до-одиннадцатом C++ живут по разным причинам.


Это вам хорошо рассказывать такие сказки если у вас в проекте только десяток человек и всё в одной организации. А если у вас сотни компонент от десятков вендоров, то переход растягивается на годы.

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


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

Так можно и про systemd рассказать, тоже был расколбас, но ничего, перетащили помаленьку.
systemd тут причём? Он полностью поддерживал почти все фишки SysvInit поддерживал во-первых, а во вторых — переписать сто тысяч строк скриптов несколько проще, чем сто миллионов строк кода на C++. Как показывает математика — примерно так на три порядка.

Речь ведь не о полной поломке совместимости уровня «вчера всё работало, сегодня ничего не работает», а о постепенной миграции.
Речь идёт именно о такой поломке. Послепенная миграция идёт всё время: в C++17 убрали auto_ptr, в C++20 — raw_storage_iterator и так далее.

Никто не заставляет компилировать библиотеки самым свежим компилятором, многие до сих пор на до-одиннадцатом C++ живут по разным причинам.
Очень мало кто живёт, на самом деле, уже. Именно потому что библиотеки собранные с _GLIBCXX_USE_CXX11_ABI=0 и _GLIBCXX_USE_CXX11_ABI=1 несовместимы.

Теоретически можно в одной библиотеки иметь и _GLIBCXX_USE_CXX11_ABI=0 и _GLIBCXX_USE_CXX11_ABI=1 (в самом libstdc++ так и сделано), но это оказывается настолько неудобно, что этим мало кто заморачивается.

В дистрибутивах библиотеки пересоберут, если получится, а где не получится, там будет старая шестёрка использоваться.
Дистрибутивы как раз самая малая из проблем. Там обычно всё из исходников собирается.

Я не знаю, можно ли будет новую и старую загружать одновременно (скорее всего, нет), и тогда да, библиотеки, собранные под разные версии стандарта вместе не уживутся, и программа, нуждающаяся в обеих, не заработает.
Вот в переходе 2004го года этого было делать нельзя — и это был полный кошмар. А уже в 2015 всё достаточно спокойно случилось — но заняло порядке 7-8 лет.

Если в каком-нибудь C++33 будет мало других фишек — можно поломать обратную совместимость и устроить этот аттракцион ещё раз. Но делать так, чтобы С++23 можно было начать пользоватьс только ближе к 2030му году — комитет не захотел.
Там проблема глубже. На libstdc++.so.6 + libstdc++.so.7 проблемы только начинаются: вам нужно собрать отдельный Boost для libstdc++.so.6 и отдельный для libstdc++.so.7… фактически каждая популярная C++ библиотека удвоится.

Поэтому обычно в рамках дистрибутива сразу переходят на новый ABI.

Заметье, что в комитете предлагали сломать ABI на уровне ядра языка. В этом случае у вашего проекта возникают проблемы с поддержкой старых платформ — в C++ вы могли пользоваться новыми языковыми фишками и [очень]старой стандартной библиотекой, но при сломанном ABI вы так больше делать не можете. Новый компилятор будет просто выдавать бинарники не линкуемые со старым стандартом. Поэтому вы будете вынуждены разрабатывать под версию стандарта поддерживаемую всеми вашими платформами. А это замедлит внедрение новых стандартов C++, что не очень хорошая идея.

Ломать ABI можно, но это должно быть осознанное решение вендора для конкретой реализации библиотеки, а не решение комитета на уровне языка. Форсирование слома ABI может быть не поддержано вендором, и мы получим стандарт C++ которым не будут пользоваться на какой-то платформе. Это очень нездоровая ситуация.

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

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

мост между ABI

extern «C»?
На самом деле здесь есть некое лукавство, ибо собственно ABI частью стандарта не является, более того, ABI очень сильно платформно-зависим (даже повсеместно применяемый Itanium C++ ABI содержит очень много архитектурно-зависимых нюансов). В моем понимании комитет может только выдать рекомендации по новому ABI, но никак не стандартизовать его. И решение по переходу на новый ABI в конечном итоге останется за вендором. Но для начала нужно реализовать поддержку нового ABI компиляторами.

На самом деле, я не вижу больших проблем с переходом на новый ABI. Ну будут в переходный период разные версии библиотек с разным ABI, и что в этом такого? Это necessary evil. Вон в винде почти каждое приложение тащит с собой свою версию VC рантайма, и никто не возмущается.

Ну это уже тогда по факту будут два разных языка типа C и С++. Ведь будут обнаруживаться глюки в старых версиях компилятора, их будут править. Потом вводить часть фишек из нового стандарта и других языков и вскоре разделение уже довольно заметным.
Думаю, со временем ранние версии языка отомрут, а код будет переписан.
Вот только время это может измеряться десятилетиями…
Кто может объяснить о каких конкретно проблемах идет речь в этом примере
X *make_x() {
  X *p = (X*)malloc(sizeof(struct X));
  p->a = 1;
  p->b = 2;
  return p;
}
До C++20 стурктура по адресу p не создавалась. Компилятор мог делать странные оптимизации, подразумевая что по p нет объекта
А есть ли случаи такого поведения в дикой природе, или этот UB был полностью «бумажным»?
Большинство известных мне компиляторов так не чудили

Теперь все обязаны не чудить
Приведите пример чудения для структуры из Вашего примера, пожалуйста.

Upd: возможного чудения, конечно.
Мне не пример UB вообще. Мне вот конкретно про данный случай. Что вызовет проблемы?

Upd: хотя фрагментация, конечно, это не форматирование, которое в качестве страшилки про UB детям рассказывается :-)
UFO just landed and posted this here
Насколько я понимаю, обращения с р как со структурой — чтение и запись полей — невалидны, потому что эта структура никогда не была сконструирована (не вызвался конструктор, the lifetime of an object has not started). Поэтому такие обращения невалидны, и если компилятор может быть уверен, что конструирования не было, он вправе выкинуть эти обращения вообще, или предположить, что значения полей имеют константное значение, удобное для оптимизации, или предположить, что этот код вообще никогда не может быть фактически вызван, или сгенерировать UD2… Вариантов много, и они вызывают разное непонятное и непредсказуемое поведение.
Пример определенно странный. Может в примере не new потому что хотят выделить одним блоком место под несколько структур за раз. Почему то же вручную задают размер… Или можно предположить, что косяк происходит с алигнментом и некорректным расчетом длинны структуры в каких то редких компиляторах, в результате чего хвост используемой в последствии структуры вылезет за пределы выделенной памяти из-за паддинга элементов структуры. Както так… Я на Шарпе просто уже года три, уже забыл про эти проблемы, могу ошибаться.
З.Ы. Мне кажется им надо не из парохода делать паровоз, а просто создать новый с+++ с новым ABI и его мучить. Кому надо-тот легко переведет программу.
Мне кажется им надо не из парохода делать паровоз,


А чем тогда будут заниматься люди с фотографии?
З.Ы. Мне кажется им надо не из парохода делать паровоз, а просто создать новый с+++ с новым ABI и его мучить. Кому надо-тот легко переведет программу.
А раскажите-ка, как в вашей альтернативной вселенной, где Pascal давно умер, а Modula-2 и её потомки процветают, живётся. Интересно же.

А то в найшей вселенной скучно: Delphi как продавался, так и продаётся, Modula-2 и Operon'ы всякие «в дикой природе» не встречаются.

P.S. Успешными в нашей вселенной были только несколько переходов: Visual Basic, Python… этап «лёгкого перевода программ» занимал миниму лет 10 (обычно ближе к 20) и приводил к успеху только тогда, когда главный вендор языка категорически отказывался поддерживать старую версию. Многие языки в результате просто «вылетели на обочину» и ими перестали пользоваться…
Delphi продается далеко не так, как продавался. А Хейлсберг стоял за C#, который продается гораздо лучше.
Delphi продается далеко не так, как продавался.
Однако же Modula-2 и Oberon продаются гораздо, гораздо хуже.

А Хейлсберг стоял за C#, который продается гораздо лучше.
Однако не имеет никакого отношения к Pascal вообще. Речь же идёт не о судьбе комитета по стандартизации C++, а о судьбе C++, как бы…
Речь в комментарии, на который я отвечал, шла про
просто создать новый с+++
.

С тремя плюсами. Будет ли Си с тремя плюсами иметь к C++ большее отношение, чем C# к Pascal — дело личной фантазии каждого.
Будет ли Си с тремя плюсами иметь к C++ большее отношение, чем C# к Pascal — дело личной фантазии каждого.
Вопрос не в отношении C+++ к C++. А в том, что далеко не всем удаётся найти идиотов, готовых «слить» свою компанию ради того, чтобы продвинуть новый модный язык.

Рассчитывать же на то, что таких идиотов найдётся много — уже и совсем бессмысленно.
а я и не говорю что всё так просто, комментами не описать всю прелесть процесса ))). Речь идет о легком переходе в виде лишь перекомпиляции библиотек и программ (если они после этого заработают), но ведь надо еще исключить коллизию со старым кодом…
Это уже получается другой язык. Тогда уж введите в него нормальное понятие string, введите понятие безразмерного char юникодовского, введите в него смарт указатели и на их базе сделайте встроенный сборщик мусора. Создайте четкие стантарты ABI для кроссплатформенности и у вас получится лучше чем Шарп ;)
Создайте четкие стантарты ABI для кроссплатформенности и у вас получится лучше чем Шарп ;)
Совершенно не факт.

Уж сколько уж было подобных попыток. Начиная с PL/I — тот тоже дожен был заменить и Fortran и Cobol… результат — пшик.
Пример: Структура не создана, значит и запись в неё не имеет смысла и можно её не производить.

Другой пример: структура не создана, а значит и последующие чтения из неё не имеют смысла и можно заменить на чтение 0.
У нее же нету пресловутой non-vacuous initialization. Выделили storage — началось lifetime. Нет?

По стандарту разрешено обращаться к участку памяти только* через указатель тип которого совпадает с "effective type" этого участка памяти. Так вот у узастка памяти выделенного malloc такого типа нет. Поэтому тот факт, что мы интерпретируем его как X нарушает strict aliasing. С этого момента даже самое бредовое поведение будет находится в рамках стандарта. Например компилятор может посчитать что код после malloc не имеет наблюдаемых эффектов за пределами функции, и выкинуть его.


    • на самом деле не только. Гуглите strict aliasing

Ничего не понял

Объектов не существует, мы работаем с памятью, которую интерпретируем как объекты того или иного типа.

О чем вообще идет речь? Что malloc будет создавать еще и таблицу виртуальных функций, а memset 0 будет её обходить стороной и работать как new без вызова конструктора? Или что?
Ну, видимо, да. Про конструкторы-деструкторы-виртуальные функции и из них вытекающее.
Ни в коем случае. Все типы, которые могут таким способом возникать должны иметь конструирущий код из нуля байт и деструктор из нуля байт.

Если тип нетривиален, то его так создавать нельзя.
Создавая объект, компилятор создает только упорядоченный блок памяти, в котором есть блок памяти, который вы интерпретируете либо как данные структуры, либо интерпретируете как указатели на методы, среди которых для классов/структур есть такое понятие как конструктор и деструктор, которые вызываются при new/delete. При malloc ничего не вызывается, а только выделяется память.
И вот в этом примере и вызывать нечего.
если честно, в данной статье написан бред, хорошо бы глянуть ссылку в стандарте, что имеется в виду, я не нашел ничего про это. То что модели памяти доработали, то это вообще к этому не имеет отношения.
Ну, бред — это сурово сказано. Но пример со структурой совершенно неудачный. Забывают люди корни, увлекаясь модными фишками :-)
Чем неудачный? Этот код нарушает правила языка С++, это не код валидной программы на С++. Пример очень жизненный и правильный, мне всё время приходится держать это в уме и иногда писать более сложный и чуть менее оптимальный код, чтоб не нарушать это правило.
нарушает правила языка С++


Какие? Конструктор тривиальный, выделили storage — lifetime пошло.
Я не к тому, что так надо писать.
выделили storage — lifetime пошло.
Это с какого перепугу-то?

Конструктор тривиальный
Тем не менее — он нигде не выхван, так что объект никогда не был создан.

Я не к тому, что так надо писать.
Проблема в том, что так, собственно, и пишут — с 70х годов… а стандарт этого не позволяет.

Собственно это просто чёткое описание для разработчиков компилоров того, что в них и так есть (все известные мне компиляторы считает, что malloc создаёт объекты… хотя по стандарту C++17 это не так).

Так что практически, для malloc'а это все не нужно… Нужно для всяких arena-аллокаторов, когда берут кусок памяти, где «жил» объект одного типа, а потом суют туда объект другого типа.

Вот тут может быть… неприятность…
Это с какого перепугу-то?


An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor. [ Note: Initialization by a trivial copy/move constructor is non-vacuous initialization. — end note ]
The lifetime of an object of type T begins when:
(1.1) — storage with the proper alignment and size for type T is obtained, and
(1.2) — if the object has non-vacuous initialization, its initialization is complete

Это из драфта 2017, под рукой прямо сейчас ничего другого нет. Но не думаю, что радикально как-то в не-драфте поменялось.
The lifetime of an object of type T begins when:
(1.1) — storage with the proper alignment and size for type T is obtained, and
(1.2) — if the object has non-vacuous initialization, its initialization is complete
лайфтайм начинается когда для объекта выделена память и завершилась его инициализация (если таковая этому классу требуется).
Совершенно верно. Структуре из примера требуется?

Upd: хотя, вообще-то, не совсем верно. Не дословный перевод :-) Но там же в тексте расшифровано, что такое non-vacuous initialization.

Upd2: вот в 20-м драфте уже «its initialization (if any) is complete (including vacuous initialization) „
Совершенно верно. Структуре из примера требуется?
хе-хе, для начала пример не гарантирует выровненность блока памяти под структуру (в зависимости от определения структуры конечно же).

Насколько я понял, формально (X*)malloc(...) были две отдельные операции — А. агностичное к каким-то там объектам выделения памяти и Б. интерпретация абстрактной памяти как объекта. Ни одна из этих операций не начинает лайфтайм объекта.
хе-хе, для начала пример не гарантирует выровненность блока памяти под структуру (в зависимости от определения структуры конечно же)


Regular malloc aligns memory suitable for any object type (which, in practice, means that it is aligned to alignof(max_align_t)), если повар нам не врет.

Ни одна из этих операций не начинает лайфтайм объекта.


Что же его тогда начинает, если «непраздной» инициализации не требуется, и память нужного размера с нужным выравниванием уже есть?

Regular malloc aligns memory suitable for any object type (which, in practice, means that it is aligned to alignof(max_align_t))
предполагая что структура не overaligned
Что же его тогда начинает, если «непраздной» инициализации не требуется, и память нужного размера с нужным выравниванием уже есть?
давайте на примере:
void* p = aligned_alloc(alignof(T), sizeof(T));
// Здесь мы просто память выделили. Еще никто не сказал что в ней будет лежать T
T* x = (T*)p;
// А здесь мы просто проинтерпретировали какую-то память как объект, мы его наверно где-то раньше создали?
T* y = (T*)p;
// Хм. Мы же точно не создали объект дважды, верно?
По сути, формально буква стандарта и компиляторы чуть-чуть по разному этот кейс трактовали. Вот и подправили стандарт чтобы не было расхождения «формально»/«на деле».
а стандарт этого не позволяет.

Изначальный пост-то про то, что теперь-то 146%, как позволяет, нет?
Ну в статье об этом не сказано, надо курить все же что имели в виду в стандарте.
Ничего не понял
Потому что не пытались.

Объектов не существует, мы работаем с памятью, которую интерпретируем как объекты того или иного типа.
С точки зрения стандарта как раз памяти-то и нет, существуют только объекты.

О чем вообще идет речь? Что malloc будет создавать еще и таблицу виртуальных функций, а memset 0 будет её обходить стороной и работать как new без вызова конструктора? Или что?
Речь о том, что поскольку описанная функция всегда вызывает неопределённое поведение, то достаточно грамотный компилятор может, например, выкинуть её из программы вообще.

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

Всё это напоминает историю с вот таким вот прелестным кодом:
int int_size() {
    int size = 1, count = 1;
    while (count > 0) {
        size++;
        count += count;
    }
    return size;
}
Так вот от момента, когда C89 разрешил компиляторами генерировать вот это:
GCC, для примера
int_size():
.L2:
    jmp     .L2
до момента, когда люди типа вас подняли вселенский вой — прошло больше 10 лет.

В данном случае разработчики старндарта встали на вашу сторону и изменили стандарт, чтобы такого не случилось…
С точки зрения стандарта как раз памяти-то и нет, существуют только объекты.


Слово storage пропало из стандарта?
Нет конечно. Где-то же объекты должны существовать. Но вы нигде и никак не можете оперировать с «сырой памятью» — только с объектами.
raw_storage_iterator и т.п.
и dynamic_cast к void*.
raw_storage_iterator удалён из C++20 — именно потому что никакого способа использовать его в варидной программе на C++ стандарт не давал.
Текущий вариант мог создавать проблемы. А мог и не создавать. Это не то же самое, что принципиальная невозможность использовать, и вообще работать с raw storage. Проблема, кстати, еще с 11-м стандартом возникла. Что не помешало raw_storage_iterator дожить до 20-го.
А мог и не создавать.
Не мог. Практически любая программа с его оспользованием вызывала UB и, соотвественно, программой на C++ не являлась.

Что не помешало raw_storage_iterator дожить до 20-го.
Если бы им реально пользовались, то его и в C++20 не выкинули бы.
Речь о том, что поскольку описанная функция всегда вызывает неопределённое поведение, то достаточно грамотный компилятор может, например, выкинуть её из программы вообще.


Молча, видимо :-)

Именно что молча, как и в случае с большинством UB. Потому-то их так и боятся.

Пример приведите. Как компилятор молча выкидывает из программы функцию, потому что считает, что там UB.

Конкретно этого примера нет, потому что спохватились вовремя.


Вот другой пример, где происходит именно выкидывание (правда, не функции, а цикла): Неопределённое поведение и теорема Ферма


А вот куда более страшный пример: Как может вызваться никогда не вызываемая функция?

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

Так что максимум что может сделать (и делает иногда) компилятор — так это превратить вашу функцию в «пустышку» (пример я приводил).

Но я тут не совсем уверен, что hobogene именно это имеет в виду.
Ооо, какая шикарная подборка настоящих живых УБ, а не обычное «винт тебе форматнёт. А потом брата убьёт.». Спасибо!
Пример вам уже приводили. Или для вас так принципиально, чтобы функция выкинулась вся целиком, вместо того, чтобы там была заглушка, на которой исполнение остановится?
Или для вас так принципиально, чтобы функция выкинулась вся целиком


Вы сами про это написали. Более, того, писали, что это будет именно из-за UB. Я попросил подтвердить примером. Если не вышло, ну и не вышло. Никто не умрет.
UFO just landed and posted this here
К сожалению (или к счастью) — нет.

Никакой, самый ужасный код, который имеется у вас в функции не может сделать программу невалидной если эта функции никогда не вызывается.

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

Так что максимум, что компилятор может сделать — вставить на место функции пустышку. Что я обычно и имею в виду, когда говорю новичкам про то, что «всю вашу функцию компилятор может просто удалить».

И таких примеров — вагон.

Но при этом почему-то именно за эту фразу hobogene зацепился и теперь носится с ней, как с писаной торбой…
UFO just landed and posted this here
Но это, конечно, все неважно. ИМХО «функция выкинулась вся» и «от функции остался один лейбл» примерно эквивалентны.
Там не лейбл. Там целый jmp остался.
UFO just landed and posted this here
Ну вот не любит gcc совсем пустые функции почему-то…

Нет, виртуальные функции тут ни при чём. Проблема тут в модели памяти С++.


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


Упрощая, как раз памяти не существует, мы работает с объектами.

Насколько я помню, с точки зрения стандарта память состоит из множества изолированных объектов, у каждого из которых могут быть свои подобъекты.
Не только с точки зрения стандарта. В E2k в «защищённом режиме» так оно, в общем, и есть: создание объекта — это привилегия ядра операционной системы, просто так его создать нельзя.

И вот когда с этим попробовали работать — как раз и выяснилось, что куча программ и библиотек к этому нифига не готовы… и, в общем, C++ стандарт отчасти тоже виноват: если на платформе есть intptr_t, то, в соотвествии со стандартом, можно указатель в него преобразовать и обратно тоже. Правда этот тип не является обязательным, так что теоретически стандарт к работе в системе, где всё на свете объекты таки готов.
UFO just landed and posted this here
Вы не можете складывать полученные числа, ожидая, что что-то разумное получится (кроме адресации внутри массивов, возможно, но я не уверен).
Зато можете, скажем, записать в файл на диск, а потом прочитать из него.

Ну или организовать XOR-связный список.

При всех этих манипуляциях у вас теряется информация о том, что в данном участке памяти хранится указатель на объект — а это чревато с точки зрения безопасности.

Можно, наверное, предоставить процедуру, которая проверит что имеющееся у вас число, которое не помечено как указатель, на самом деле, указывает на валидный объект, к которому вы имеете право доступа и превратит число в указатель… но представьте себе с какой скоростью при таком подходе будет работать тот же XOR-связный список!
Объект занимает, тем не менее, связную область storage ненулевого размера. Так что указатели в этом примере, наверное, вообще не особо при чем.
Хм… А чем формально вышеописанный код отличается от

file1.cpp
X* malloc_x() {
    return (X*)malloc(sizeof(struct X));
}


file2.cpp:
extern X* malloc_x();

X *make_x() {
  X *p = malloc_x();
  p->a = 1;
  p->b = 2;
  return p;
}


В измененном примере мы вызываем функцию, которая возвращает указатель на объект типа X, и компилятор понятия не имеет, каким образом он получен. Разница только в том, что в исходном примере компилятор видит слово malloc и думет: «ага, мы тут память неинициализированную выделяем», хотя никто ему не давал права судить, что именно malloc() делает семантически.

Функция malloc как часть стандартной библиотеки описана в стандарте, а значит известна компилятору. Это и даёт право судить о её семантике.

Это сомнительное утверждение. На этапе компиляции нет никаких гарантий, что используется именно стандартная библиотека, поэтому malloc как функция, задекларированная средствами языка, не несет никакой информации о семантике. То, что компилятор делает некоторые (пусть даже верные в 99 процентах случаев) предположения о семантике с целью последующей оптимизации, — это допустимо, но должно быть четко отражено в документации на компилятор, а по-хорошему должно управляться опциями (кстати, интересно, будет ли, скажем, gcc это оптимизировать с -nostdlib?), и должна быть возможность получать предупреждения о подобных оптимизациях.
Это пример сильно отличается от приведенного примера с int_size(), где причиной оптимизации является неопределенное поведение, четко описанное в семантике языка

Ну вот в glibc функция malloc объявлена с атрибутом __attribute__ ((__malloc__)).


Как вы думаете, этого достаточно чтобы компилятор понял, что malloc — это именно malloc, а не что-то ещё?

В данном случае сложно сказать, что является триггером, так как конкретный вышприведенный пример мне не удалось скомпилировать так, чтобы компилятор выкинул инициализацию полей. Т.е. я пробовал и gcc, и clang разных версий — не получается. Кстати, пример можно легко модифицировать с использованием realloc(), у которого атрибута нет (атрибут указывает на невозможность алиасинга, что для realloc'a неверно).
Но то, что стандарт поправили — это хорошо.
так как конкретный вышприведенный пример мне не удалось скомпилировать так, чтобы компилятор выкинул инициализацию полей

Так тут в комментариях же уже раз 10 написали, что стандарт поправили вовремя. То есть раньше, чем компиляторы начали портить код.

Создается не структура, а создается блок памяти размером структуры.
Далее объектом является указатель, который мы интерпретируем как указатель на структуру, который имеет свое время жизни на стеке.

Правильно ли я понимаю, что данная правка обязывает интерпретировать этот блок памяти как структуру при приведении void* к struct * и -> считать обращением к элементам структуры, что и делали ранее все компиляторы, просто сейчас это прописали явно и ввели запрет интерпретировать как-то иначе, хотя я ума не приложу как можно интерпретировать иначе?
Да, всё верно. Это теперь принято называть implicit construction, и оно срабатывает если у типа есть тривиальный конструктор и деструктор

Подскажите, а обсуждалась ли в контексте std::hash возможность отделить описание в объекте что хешировать от конкретного хешера?

Не очень понял вопрос. Приведите пожалуйста пример

Я имею ввиду разделение на Hasher и Hashable, что-то вроде такого


// Хешер, считает хеш
template<typename T>
concept Hasher = requires(T a) {
    { T::hash_type; }
    { a.hash() } -> std::convertible_to<T::hash_type>;
    { a.append(std::span<std::byte>{}) };
};
// Тип, который можно хешировать
template<typename H, typename T>
concept Hashable = Hasher<H> && requires(H h, T t) {
    { append_hash(h, t) };
    // ... или ...
    { t.hash(h); }
};

// ... где-то в коде
struct Foo {
    int first;
    int second;
};

template<Hasher H>
void append_hash(H& hasher, Foo const& foo) {
    append_hash(hasher, foo.first);
    append_hash(hasher, foo.second);
}

int main() {
    Foo foo { 21, 42 };
    std::hasher hasher;
    append_hash(hasher, foo);
    std::cout << "The hash of Foo { 21, 42 } is " << hasher.hash() << "\n";
    return 0;
}

Т.е. тип объявляет только какие байты и как участвуют в хеше — но не как считается хеш. А хешер — как хеш считается из байт, но не откуда и как эти байты берутся.

Идея в том, чтобы иметь какие-то объекты Hasher, которому уже что-то передавать в тот же operator(), чтобы получить какой-нибудь хеш. Я такую точно от кого-то видел (если не ошибаюсь, то от Jossuttis (не знаю, как его фамилия правильно пишется)), но не могу найти бумаги.

А что с контрактами? Эпопея закончилась? Я имею ввиду, выбрали приоритетный подход, или все еще в поиске?

В самом разгаре. Пока всё неочевидно и кажется что 10 раз поменяется
мало того что изменение ломает код как минимум двух компаний
всегда найдется компания, у которой что-то сломается, и которые против изменений ради status quo
Обработчики контракта могут кидать исключение, если исключение кинуть из noexcept функции — будет std::terminate и приложение рухнет
если только проверки предусловий контракта не вынести перед вызовом функции. Тогда и логика будет «noexcept функция не кидает если контракт не нарушен», что логично и просто, и оптимизировать проверки контрактов компилятору будет проще
> всегда найдется компания, у которой что-то сломается, и которые против изменений ради status quo

И это хорошо до тех пор, пока не ломается код вашей компании, или код любимого вами приложения.

> Тогда и логика будет «noexcept функция не кидает если контракт не нарушен», что логично и просто, и оптимизировать проверки контрактов компилятору будет проще

Вас не смущает, что с таким подходом std::is_nothrow_* трейты вам будут врать и вы не сможете написать эффективные библиотеки? И что производительность вектора может ухудшится раз в 10?
И это хорошо до тех пор, пока не ломается код вашей компании, или код любимого вами приложения.

мои любимые приложения и так постоянно ломаются (
И что производительность вектора может ухудшится раз в 10?
так наоборот же. Сейчас is_nothrow_* трейты для методов типа std::vector::back() врут, возвращая false, хотя на самом деле (в большинстве реализаций стандартной библиотеки) там никогда не возникнет исключение. А noexcept специализации всегда (когда они есть) реализуют более эффективный алгоритм.

Ну и хочется иметь noexcept для варианта релизной сборки без рантайм проверок контрактов
std::vector благодаря трейту is_nothrow_move_constructible решает, копировать ему элементы при resize, или же просто перемещать.

Если трейт начнёт врать, то вектор возможно что всегда будет копировать. А это безумные замедления, никакие мнимые преимущества от noexcept их не перевесят.

Кстати, добавьте noexcept к vector::back прям сейчас… и вы не увидите разницы в кодгене для GCC (остальные вендоры ленятся с правильной реализацией исключений)
Если трейт начнёт врать, то вектор возможно что всегда будет копировать. А это безумные замедления, никакие мнимые преимущества от noexcept их не перевесят.
трейт начнет врать если мы начнем навешивать контракты на мув конструкторы/операторы присваивания. А это скорее странный/нежелательный юзкейс нежели типовой
Кстати, добавьте noexcept к vector::back прям сейчас…
vector::back сравнительно хорошо инлайнится, а для более сложных функций разница будет заметной.
остальные вендоры ленятся с правильной реализацией исключений
да с исключениями вообще всё очень плохо. И поэтому noexcept так важен
кстати, можно же еще вопрос иначе поставить: может быть разрешить стандартным библиотекам усиливать noexcept'ness гарантии для не кидающих функций? Т.е. пометить набор функций в стандарте как noexcept(/*implementation-defined*/)?
UFO just landed and posted this here
UFO just landed and posted this here
И тут разработчики MS VC «А я говорил, говорил!» :D
UFO just landed and posted this here
Используйте это на свой страх и риск. Как будут выглядеть стандартные модули ещё не решено, только зарезервированы имена начинающиеся с std. Если к C++23 комитет решит сделать несколько другие имена модулей, код с вышеозвученными MS модулями немного поломается.
А такие конструкции
import <iostream>;
как относятся к стандарту?
Импортирование C++ хедеров обязано работать в C++20. import <iostream>; — это правильно, безопасно.

Но не так быстро как import std.iostream;, который появится только в C++23
Если к C++23 комитет решит сделать несколько другие имена модулей, код с вышеозвученными MS модулями немного поломается.
надеюсь если захотят поменять реализацию аргумент MS а-ля «а мы уже вендорлокнули» проигнорируют?
Ну если вариант MS будет более менее, а остальные вариантов лучше не предложат, то могут принять MS вариант, ничего не мешает.
Увидим. Хороший будет тест на Microsoft. Исторически у них было… по разному.

Например вот такая вот программка:
#include <stdio.h>
int arr[42];
int main( void ) {
     printf( "%u\n", sizeof( &arr ) );
     return( 0 );
}
Стала вести себя «по правилам» аж в Visual Studio 2008. Visual Studio 2005 всё ещё ведёт по заветам K&R, а не стандарта C89.

Так что не удивлюсь если эти модули будут лет 10 приводиться к стандарту…

А как ввела себя эта программка в VS 2005?

Ну если вариант MS будет более менее, а остальные вариантов лучше не предложат, то могут принять MS вариант, ничего не мешает.
co_co_co_routines заехали такими потому что слишком поздно спохватились менять на нормальный синтаксис; предложение по модулям от МС не вносило буквально ничего в С++ — собственное расширение (которое послужило основой реализации) они использую преимущественно для биндингов к шарпу, виндовому API и прочим COM/OLE. Для них выгодно чтобы их решения становились стандартными даже в ущерб всей индустрии поэтому к предложениям от MS лучше относиться осторожно

Ну здрасьте! Там в Coroutines TS именно что полностью переделали предложение от MS, в том числе заменили синтаксис.


Оригинальное предложение было с совершенно другим синтаксисом.

Антон, с большим уважением отношусь к твоему вкладу в наше светлое будущее.


Мои впечатления от поста: информация полезная, но написано сложно. Пара примеров:
1) Пример со структурой с C. Насколько я понял, то мораль: "не делай так, снег в башка попадёт". Не совсем понятно, что изменилось: не было специфицировано/ теперь специфицировано, могут ли теперь компиляторы делать "странные оптимизации"? При использовании gcc/clang я не смог добиться от make_x() чего-то некорректного.
2) Иногда порядок слов сложный. Следующая фраза, как пример: "Вы не сможете воспользоваться в проекте с C++23 библиотеками, собранными более ранними версиями C++"


И пара вопросов:
1) Как проходит голосование, например, за такую фундаментальную вещь, как смена ABI:
простым перевесом голосов?
2) Что планируется в направлении Reflection?

1) Согласно букве стандарта раньше было нельзя так делать, но все делали. С C++20 теперь так делать можно.
2) Постарался упростить. Если найдёте ещё сложные предложения — пишите в личку, поправлю.

Ответы на вопросы второй части:
1) да, фактически прочтым перевесом голосов
2) Я очень надеюсь на Reflection в C++23, шансы увидеть его в ближайшем будущем высоки

Разве понятие "implicitly creating objects" вошло в C++20? p0593 ведь отложили до C++23.

Вошло в C++20. Успели в последний момент :)
С включением модулей в C++ 20 можно ожидать какое-то стандартное решение менеджера пакетов для плюсов?
т.е. что-то наподобие maven для jvm или cargo для rust'а

Над пакетными менеджерами работают, но они никогда не зависели от успеха модулей. Так что даже без модулей, работа над стандартными пакетными менеджерами продолжалась бы.
Пока что можете использовать Conan или на крайняк Vcpkg. Они точно не являются стандартными с точки зрения наличия в Стандарте, но сейчас де-факто стандарт, как CMake. Хорошо это или плохо — решать вам.

"Вот, например, такой код абсолютно валиден для C:..."


Нет, он не валиден для C. Язык С в вашем примере не будет знать, что такое X.

Угу. Для C нужно писать:
typedef struct X { int a, b; } X;

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

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

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

Если вдруг надо вынести код в библиотеку — используем C-интерфейсы между модулями. Всё. Работает с любой версией любого компилятора.
Выставлять наружу C++ объекты тоже, в принципе, можно, но будет работать только с той же версией того же компилятора и с теми же параметрами сборки, поэтому имеет смысл только в том случае, когда контролируем всю кодовую базу и можем всё пересобрать при необходимости, т.е., в основном, «только для внутреннего потребления».

Банальности уровня «мойте руки перед едой».

А теперь, оказывается, в дикой природе нашлись уникумы, решившие, что протаскивать всё через C-интерфейсы сложно, а поэтому давайте закладываться в публичных интерфейсах на недокументированное поведение. Оно же работает сейчас, а значит, очевидно, будет работать всегда.
Казалось бы — да и хрен с ними, да? Естественный отбор же, да?

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

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

А при том, что новые фичи не всегда можно впихнуть в старое представление, а заседающим в комитете разработчикам компиляторов проще слать лесом любые пропозалы, «ломающие ABI» (которого нет), чем вызывать на себя гнев пользователей, не читающих документацию. Тормозя развитие языка и прогресс.

Печаль.
Если вдруг надо вынести код в библиотеку — используем C-интерфейсы между модулями
мы тут вообще-то про с++. Да и специфичные для плюсов особенности ABI в си не завернешь
мы тут вообще-то про с++
Да вы что, я и не заметил.
Ок, «интерфейсы с Trivial & Standard Layout типами данных». Так понятнее?

Завернуть можно всё, вопрос в желании.
Ок, «интерфейсы с Trivial & Standard Layout типами данных». Так понятнее?
а еще без перегрузок, исключений, шаблонов, ссылок и (для одинакового манглинга между компиляторами) с extern «C». То есть на си а не плюсах.
Завернуть можно всё, вопрос в желании.
откуда ж возьмется желание пихать сишную прокладку на каждом стыке двух бинарей?
Банальности уровня «мойте руки перед едой».
Вот нифига не банальности.

Выставлять наружу C++ объекты тоже, в принципе, можно, но будет работать только с той же версией того же компилятора и с теми же параметрами сборки, поэтому имеет смысл только в том случае, когда контролируем всю кодовую базу и можем всё пересобрать при необходимости, т.е., в основном, «только для внутреннего потребления».
Вы сейчас описали ситауцию в Windows. А вот в мире Linux и Unix — всё было совсем по другому.

Хотя разные версии стандартной библиотеки и существуют, но этих версий относительно немного: в версии gcc 3.4 в 2004м году (это, на минуточку, 16 лет назад) и в gcc 5.1 в 2015м она была поломана. И во втором случае — это было сделанно именно из-за изменений в стандарте C++, что, как несложно заметить, задержало внедрение C++11 (и так-то потребовавшего нескольких дополнительных лет разработки) ещё на 4 года (при этом ещё и переход достаточно мягкий был: вы можете использовать в GCC 5+ два вида ABI — «старый» из GCC 3.4 и новый из GCC 5.0… в одной программе). Потом ещё пару лет GCC 5+ использовался в режиме совместимости.

И вот именно повторения этой ситуации (затягивание внедрения новой версии C++ лет так на 5-7) комитет и решил избежать…

А теперь, оказывается, в дикой природе нашлись уникумы, решившие, что протаскивать всё через C-интерфейсы сложно, а поэтому давайте закладываться в публичных интерфейсах на недокументированное поведение.
Ало, ало, ало. Вы о чём говорите ребята? Вот документ, там всё стандартизовано и описано. Его поддерживает и GCC и ICC и новомодный Clang…

Казалось бы — а причем здесь вообще стандарт, если сохранение бинарного представления — это забота компилятора?
Притом, что изменения в стандарте могут быть несовместимы с бинарным представлением компилятора, однако. Пример — как раз C++11 и строки: до C++11 строки могли использовать COW-оптимизацию, а в C++11 появились новые требования, которые поставили эту оптимизацию «вне закона». В результате — несколько лет задержка со внедрением новой версии C++.
А вот в мире Linux и Unix — всё было совсем по другому.

Ну…
Например из man gcc (секция про -fabi-version):


Version 1 is the version of the C++ ABI that first appeared in G++ 3.2.

Version 2 is the version of the C++ ABI that first appeared in G++ 3.4, and was the default through G++ 4.9.

Version 3 corrects an error in mangling a constant address as a template argument.

Version 4, which first appeared in G++ 4.5, implements a standard mangling for vector types.

Version 5, which first appeared in G++ 4.6, corrects the mangling of attribute const/volatile on function pointer types, decltype of a plain decl, and use of a function parameter in the
           declaration of another parameter.

Version 6, which first appeared in G++ 4.7, corrects the promotion behavior of C++11 scoped enums and the mangling of template argument packs, const/static_cast, prefix ++ and --, and a
           class scope function used as a template argument.

Version 7, which first appeared in G++ 4.8, that treats nullptr_t as a builtin type and corrects the mangling of lambdas in default argument scope.

Version 8, which first appeared in G++ 4.9, corrects the substitution behavior of function types with function-cv-qualifiers.

Version 9, which first appeared in G++ 5.2, corrects the alignment of "nullptr_t".

Version 10, which first appeared in G++ 6.1, adds mangling of attributes that affect type identity, such as ia32 calling convention attributes (e.g. stdcall).

Version 11, which first appeared in G++ 7, corrects the mangling of sizeof... expressions and operator names.  For multiple entities with the same name within a function, that are
           declared in different scopes, the mangling now changes starting with the twelfth occurrence.  It also implies -fnew-inheriting-ctors.

Version 12, which first appeared in G++ 8, corrects the calling conventions for empty classes on the x86_64 target and for classes with only deleted copy/move constructors.  It
           accidentally changes the calling convention for classes with a deleted copy constructor and a trivial move constructor.

Version 13, which first appeared in G++ 8.2, fixes the accidental change in version 12.

То есть не все так однозначно, если хочется быть уверенным в надежности работы программы,
и между частями кода используется C++ ABI, я бы все одной версией g++ собрал,
на всякий случай.

Так и хочется спросить: у вас вообще что по марксизму-ленинизму было? Потому что ваше творческое цитирование уж очень напоминает труды работников этой «науки», которые могли что угодно цитатами, надёрганными из Маркса-Энгельса обосновать.

То есть не все так однозначно, если хочется быть уверенным в надежности работы программы
С надёжностью работы вообще проблем нет. То, что вы тут накопали — это хитрые случаи name manglingа.

Оно влияет только лишь на имена. А почему я про специалистов по марксизму-ленинизму вспомнил — так это потому что вы привели огромную цитату (и даже под спойлер не спрятали), которая включает в себя один раздел из man gcc — но при этом опустили ровно одну строчку. Последнюю. А что ж в этой строчке написано-то?

А вот что:
See also -Wabi.

И что ж такого интересного вы можем увидеть в раздере про -Wabi? А вот что:
-Wabi (C, Objective-C, C++ and Objective-C++ only)
Warn when G++ it generates code that is probably not compatible with the vendor-neutral C++ ABI. Since G++ now defaults to updating the ABI with each major release, normally -Wabi will warn only if there is a check added later in a release series for an ABI issue discovered since the initial release. -Wabi will warn about more things if an older ABI version is selected (with -fabi-version=n).

Там ещё много чего написано — но вообще вся секция -Wabi посвящена описанию того, как линковать версии C++-кода с разными -fabi-version (и, возможно, собранные разными компиляторами).

А буквально следующая за вами процитированной секцией — это секция -fabi-compat-version=n… тоже с этим связанная.

То есть не все так однозначно, если хочется быть уверенным в надежности работы программы, и между частями кода используется C++ ABI, я бы все одной версией g++ собрал, на всякий случай.
Ну если у вас есть исходники от всех компонент, то да, возможно. Но так-то нередки случаи, когда не только части приходится разными версиями собирать, но и разными компиляторами (g++, clang++, icc). И это всё полностью поддерживается, а не как нам тут вещают… про «недокументированное поведение»…
С надёжностью работы вообще проблем нет. То, что вы тут накопали — это хитрые случаи name mangling

Э… "name mangling" такая же полноправная часть ABI как и все остальное. И то что приложение не запуститься пожаловавшись на ненайденный символ или одна из функций отвалилась, потому при вызове функции не удалось подгрузить плагин такая же проблема как и падение программы из разного размера объектов или их разного выравнивания (кстати там и выравнивание упоминается, а не только mangling).

Именно потому и существует опция -Wabi, способная на это отреагировать. И она настроена сегодня, по умолчанию на поддержку совместимости с GCC 5.x не случайно: из-за того, что C++11 поломал поддержку обратной совместимости как раз переход с GCC 3.x/4.x на GCC 5+ несколько затруднён.

Но если вам нужно — то можно установить -Wabi=2.

Что касается выравнивания, то проблемы действительно возможны, но в одном, очень редком случае — при использовании decltype(nullptr)std::nullptr_t это не относится) — то только на процессорах, отличных от x86 и arm.

Что скорее говорит о дотошности разработчиков, чем о вероятности реальных проблем: в MSVC я видел куда большие отличия между сервис-паками, чем у GCC между версиями 3.4 и 9.2…
Вы сейчас описали ситауцию в Windows. А вот в мире Linux и Unix — всё было совсем по другому.
Ну да, потому что там, грубо говоря, на всю экосистему один компилятор и одна библиотека, можно упарываться от души.

Вот документ, там всё стандартизовано и описано
Речь не столько о calling convention или там virtual table layout (это всё десятилетиями прекрасно работает в том же COM), сколько о структурах данных.
Пример: std::vector можно реализовать как { BlockBeginPointer, DataEndPointer, BlockEndPointer }, а можно и как { BlockBeginPointer, DataSize, BlockSize }. Документ этого не оговаривает, стандарт этого тоже не оговаривает. Ваш пример с COW сюда же.

И вот именно повторения этой ситуации (затягивание внедрения новой версии C++ лет так на 5-7) комитет и решил избежать…
… и теперь каждые 3 года мы имеем новый стандарт с новыми способами выстрелить себе в ногу, потому что старые расширять нельзя из-за этого вашего ABI, как и баги исправлять.

Пройдет ещё 10 лет и в стандарте появятся какие-нибудь sane_regex, fast_unordeded_map, mutable_initializer_list и т.п. потому что прогресс неизбежен, но то, что есть — священная корова. Не трожь, а то GCC сломается.

А через 20 лет 80% стандартной библиотеки окажется морально устаревшей, но будет висеть мертвым грузом, ибо совместимость.

«There should be one-- and preferably only one --obvious way to do it» — это, увы, не про C++.
Ну да, потому что там, грубо говоря, на всю экосистему один компилятор и одна библиотека, можно упарываться от души.
А вот давайте не рассказывать сказок? На обычном Linux у вас четыре компилятора (GCC, Clang, ICC и PGI), на AIX — есть IBM XLC++, на SunOS — Oracle C++. Собственно бо́льшая часть компиляторов в списке — это как раз компиляторы под Linux и Unix, не под Windows.

Пример: std::vector можно реализовать как { BlockBeginPointer, DataEndPointer, BlockEndPointer }, а можно и как { BlockBeginPointer, DataSize, BlockSize }. Документ этого не оговаривает, стандарт этого тоже не оговаривает.
А вот это уже как раз так как вы сказали: стандартная библиотека — часть системы, потому как она устроена — так и будет.

«There should be one-- and preferably only one --obvious way to do it» — это, увы, не про C++.
Да, это про язык у которого есть три способа разбора командной строки (getopt, optparse и argparse) — и это несмотря на не так давно устроенный большой %$%@#ц, когда они потратили 10 лет на то, чтобы сделать несколько дастаточно бессмысленных изменений в языке…

А через 20 лет 80% стандартной библиотеки окажется морально устаревшей, но будет висеть мертвым грузом, ибо совместимость.
Это нормально. Есть языки, которые считают, что ломать совместимость это не больно и не страшно (скажем D) — ну… у них отличный язык, замечательная системная библиотека и очень мало пользовать. Всё, что про них нужно знать: не использовать. Никогда и нигде.

А есть языки, где «80% стандартной библиотеки морально устарела»… но ими люди пользуются — и среди них можно выбирать.

Как мы видим Python и C++ — относятся именно туда (чтобы там ни говорили про дзен разработчики первого).

P.S. А есть ещё и такой фактор: если у вас системная библиотека, где 80% — legаcy, то вы тартите это 80% места на диске впустую. Но если у вас каждая динамическая библиотека несёт в себе свою копию библиотеки C++, то при десятке библиотек — 90% места тратится впустую! А 90% — это таки хуже, чем 80%…
А вот давайте не рассказывать сказок?
«Грубо говоря». Они, несомненно, есть, но какова их рыночная доля?

это про язык у которого есть три способа разбора командной строки
«А у вас негров линчуют»? Я далёк от мнения, что Python идеален и не привожу его в пример, но идеи в Zen вполне здравые. Лучше перенимать у других хорошее, чем плохое, не так ли?

А есть языки, где «80% стандартной библиотеки морально устарела»… но ими люди пользуются
Ну да, the ones people complain about and the ones nobody uses.
Однако, у того же автора есть и другая цитата, про much smaller and clearer language struggling to get out. Рант, в основном, о том, что в языке достаточно легаси на уровне кода, если тянуть за собой еще и бинарное легаси — 'get out' не наступит никогда: в C++23 решили ABI не ломать «с небольшим перевесом», а в 26 решат не ломать единогласно, потому что:
— за 6 лет никто и не почешется подготовиться к этому ломанию
— кода, верующего в ABI, станет тупо вдвое больше.
«Грубо говоря». Они, несомненно, есть, но какова их рыночная доля?
А на Windows — что? Есть какой-то компилятор, у которого рыночная доля сравнима с Microsoft C++???

Я далёк от мнения, что Python идеален и не привожу его в пример, но идеи в Zen вполне здравые. Лучше перенимать у других хорошее, чем плохое, не так ли?
Да-да-да. «Делай, как я говорю, а не так, как я делаю сам.»

Плохая идея. Да, идеи в Zen звучат неплохо — но если бы они ещё и были бы осуществимы — так это было бы и совсем хорошо.

Я, как бы, привожу Python в пример не потому что я его не люблю, а потому если что даже язык, который приводит приведённую вами фразу в качестве руководства к действию на самом деле ей не следует… значит тому есть какие-то причины — ведь правда?

А про «get out»… поживём, увидим. Мне кажется самая большая проблема C++ — в том, что он сейчас поддерживается «либо всё, либо ничего». Если сделать как-то так, чтобы программа могла явно заказывать опциональные компоненты (а по умолчанию устаревшие шняги были бы недоступны), то это открыло бы путь к изменениям… хотя всё равно непонятно как можно было бы изменить тот же std::string ещё раз, когда на нём уже куча всего построена…
Есть какой-то компилятор, у которого рыночная доля сравнима с Microsoft C++???
Конечно есть — Microsoft C++ предыдущих версий. Каждая со своим рантаймом и своим ABI, вопросами совместимости которого до недавнего времени в MS не заморачивались.

А про «get out»… поживём, увидим
Я внезапно осознал, что был в корне неправ, get out не нужен. Больше легаси — сложнее язык — выше порог входа — меньше конкуренция — работа будет всегда. Ура комитету!
Конечно есть — Microsoft C++ предыдущих версий. Каждая со своим рантаймом и своим ABI, вопросами совместимости которого до недавнего времени в MS не заморачивались.
Ну то есть вся проблема существовала только и исключительно благодаря Microsoft'у. Причём что самом смешное, для себя-то, любимых, они всё сделали правильно.
UFO just landed and posted this here
Можете посмотреть на CPython. Много писанины, но не rocket science.
А я бы с интересом посмотрел, зачем вам таскать thread или graph между модулями.
С моей колокольни полная поддержка юникода важнее всего вышеперечисленного. Будем ждать…
А что, до сих пор не завезли? Ууу блин:(
Не завезли и не завезут пока в C++ не появятся опциональные компоненты и эту поддержку нельзя будет объявить опцией.

Потому что там требуются многомегабайтные таблицы, превышающие по объёму всю сегодняшнюю стандартную библиотеку раз так в 10. Причём нужен ещё и стабильный ABI, а разработчики libicu (библиотеки от Unicode Consortium) его разрабатывать отказываются.

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

Почему-то во многих других более молодых языках эту проблему как-то решили. Странно. Тем более странно при вагоне желающих. С сегодняшними успехами краудфандинга не дожно быть проблем и со сбором денег на реализацию. Загадка.
UFO just landed and posted this here
Я не знаю языков с полной поддержкой уникода. Где её решили?

Ну там всякие D, Go, Python… Возможно юникод настолько необъятен, что 100% покрытия в языке нет нигде, не знаю. Однако у большинства всё значительно лучше, чем в C++.
UFO just landed and posted this here
Однако у большинства всё значительно лучше, чем в C++.
Я сказал, что у большинства значительно хуже, чем в C++.

Искать подстроку в UTF-8 строке можно и функциями из 70х годов (там UTF-8 устроен, тут никакой поддержки не нужно), а если вы, скажем, хотите без учёта регистра искать… ну покажите мне язык, где это можно просто сделать не перепутав «I» и «i» в Турецком языке?

Для справки: в Турецком языке есть две разные буквы — «I/ı» (без точки) и "İ/i" (с точкой). Соответственно «I» и «i» — это разные буквы.

Большинство языков позволяют с юникодом сделать нечто, что работает «часто, но не всегда»… это один из самых худших вариантов: уж лучше пусть оно на тестах упадёт, чем у заказчика…
покажите мне язык, где это можно просто сделать не перепутав «I» и «i» в Турецком языке?

Здесь расписана эта проблема. В случае «I/ı İ/i» один и тот же символ с одинаковым кодом будет конвертироваться по-разному в зависимости от языка (локали). Там же по ссылке написано и что следует предпринять для решения проблемы, но в общем случае, имея символ и не зная язык, не существует однозначного метода преобразования регистра.

Единственное, что я не понял, так это почему в юникоде не сделали для турецкого языка специальные символы «i» и «I», а используют знакоместо из латиницы. Ведь если не пользоваться латиницей для турецких букв, то и проблема уйдёт сама собой.

Забавный случай, но при чём тут язык программирования? Это скорее проблема юникода.
Единственное, что я не понял, так это почему в юникоде не сделали для турецкого языка специальные символы «i» и «I», а используют знакоместо из латиницы.
Потому что Latin-5 и CP857 так устроены и если бы они были устроены иначе, то Турки бы тупо отказались бы переходить на Unicode.

Если не пользоваться латиницей для турецких букв, то и проблема уйдёт сама собой.
Эта проблема уйдёт, но останется куча других. Возьмите немецкий к примеру. Там в телефонных справочниках «Müller, A» идёт перед «Mueller, B», а «Mueller, B» идёт перед «Müller, C». Классно, правда?

Да возьмите даже, возможно, более близкую вашей душе кириллицу: В Украинском языке "І/і" находится где-то в начале алфавита, между «И/и» и "Ї/ї", а в казахском — между «Ы/ы» и «Ь/ь». И? Как прикажете это всё сортировать?

Забавный случай, но при чём тут язык программирования? Это скорее проблема юникода.
Это может быть чья угодно проблема, но пока она не будет решена — никакой поддержки юникода в C++ не будет.

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

Но в случае с C++ этот подход не прокатывает. По двум причинам:
1. В комитете по стандартизации — полно народу из самых разных стран и уж что-нибудь «этакое» они припомнят однозначно. Вариант, который турков лишает их любимой буквы шансов пройти через комитет по стандартизации не имеет никаких.
2. На большинстве платформ (более-менее на всех популярных, кроме Windows) никакая особая поддержка не нужна в принципе, так как UTF-8 устроен так, что функции имеющиеся в C и C++ с момента их создания — вполне работают.

Пока мы не касаемся вот всех этих разбиений текста на слова (да-да, есть языки, где это непросто — современный Китайский, к примеру: там пробелов нет и разбиение текста на слова делается по словарю), сортировок и больших-малых букв.

А вот если мы хотим всё это поддерживать…

В общем история — всегда об одном и том же: на самом деле никому поддержка Unicode в C++ не нужна. То есть есть много желающих ей воспользоваться, но чего они реально хотят — так что чтобы, грубо говоря, регулярные выражения на русском (эстонском, китайском или ещё каком другом языке) работали. Ну или слова нормально сортировались. А может цифирьки печатались бы… когда их просят написать proposal — они иногда что-то пишут, но «напрочь срезаются» на вопросах «а как это будет работать с монгольским» (напомню что там слова пишутся по вертикали) или «как мы совместим это с корейским?» (там слова «квадратиками» по спирали пишутся).

И вот после этого и выясняется, что пользоваться — хочется много кому, разрабатывать пропозал — не хочется никому. Unicode Consortium свою библиотеку, правда, поддерживает… но она в стандарт ну аж никак не лезет, ибо они наразарабатывали ах 60 с лишним версий этой библиотеки и она не имеет стабильного API (то есть какая-нибудь версия 40 от версии 60 может отличаться радикально и быть несовместимой ни в какую сторону).

Так что… желающих пользоваться — вагон, желающих что-то делать… нету. А если нету таких желающих то откуда поддержке в стандарте взяться?

Стандарт ведь не эльфы разрабатывают…
Турки бы тупо отказались бы переходить на Unicode.

Так себе аргумент. Идея юникода, в отличие от кодовых страниц, была в том, чтобы можно было использовать строки без необходимости знания языка строки и даже совмещать разные языки в одной строке и даже разные символы в одном слове. Если путём добавления двух символов можно получить автоматом и преобразования регистра, то какой смысл туркам от этого отказываться, ведь они ничего не теряют, но приобретают (а сейчас они теряют серьёзно — в турецкой локали не работает нормально английский, в английской — турецкий). Ведь это реально серьёзнейший косяк, потому что это безумие — хранить язык для каждого слова отдельно. А иначе, выходит, регистр правильно преобразовать не получится.

Как прикажете это всё сортировать?

С сортировкой всё просто понятно, необходимо всякие местные особенности учитывать, здесь уже и локаль имеет смысл, но с этим я не вижу проблемы. Во-первых сортировка это довольно затратный процесс, во-вторых выставить локаль для всех строк одновременно для упорядочения не приведёт ни к неудобствам ни к каким-то серьёзным трудностям. Другое дело — преобразование регистра. Что делать если у меня разные языки? Хранить язык для каждого слова и менять локаль в процессе поиска для каждого слова или для сортировки? Это же бред. То есть так как оно сделано в в юникоде и в С++ сейчас выходит просто неправильно. Ужас ужас.

Это может быть чья угодно проблема, но пока она не будет решена — никакой поддержки юникода в C++ не будет.

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

Либо у пользователя можно спросить (Windows, скажем, использует один метод для всех имён файлов на диске, хотя для разных дисков могут быть выбраны разные варианты).

То есть так как оно сделано в в юникоде и в С++ сейчас выходит просто неправильно.
Почему неправильно? Сделано, то, что возможно. Вы же сами нашли ствтью, где написано, что немцы считают, что DUERST и Dürst — это одно слово… а финны так не считают. И при этоми те и другие уверены, что используют латинницу «правильно».

Но всё равно, лучше плохонький стандарт с недостатками, чем вообще ничего.
Как показывает практика — нет.

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


Тем не менее это необходимо, если вы хотите делать какие-то нетривиальные преобразования.

Преобразование регистра это всё-таки тривиальное преобразование, нетривиальным в случае iI его сделали «странные» (мягко говоря) решения с кодировкой. Решается просто путём ввода нового символа. Может есть какие-то другие, не такие очевидные случаи, не знаю… И то что большинство разработчиков игнорирует такие «фичи» юникода, вполне ожидаемо.
Либо у пользователя можно спросить (Windows, скажем, использует один метод для всех имён файлов на диске, хотя для разных дисков могут быть выбраны разные варианты).

Ну так не поможет же, если на диске есть и турецкие и английские названия, потому что преобразование регистра не будет работать либо для английского, либо для турецкого. Хранить ради этого язык для каждого имени файла что-ли? А если два слова в названии, одно английское, другое — турецкое (например файл с переводом). Тогда нужно переключатели языка прямо в строку вставлять (хотя в данном случае проще вставить новые символы)… Что опять же, должно решаться на стороне юникода, потому что язык слова в контексте преобразования регистра это не свойство локали, это свойство слова!!! Хранить язык в локали для преобразования регистров — ошибка юникода. И решения здесь только два — либо хранить язык в виде невидимых переключателей языка в строке, либо в каждом символе. Хранить в локали просто не получится.
немцы считают, что DUERST и Dürst — это одно слово… а финны так не считают

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

Как только вы что-то добавили в стандарт — на это в очень короткое время оказываются завязаны десятки и сотни тысяч библиотек. Править потом что-либо — очень сложно.

Без юникода сегодня почти ничего нельзя ничего сделать, поэтому всё равно десятки и сотни тысяч библиотек завязаны на то или иное решение. И всё равно, рано или поздно стандарт будет принят и эти сотни тысяч библиотек всё равно придётся переделывать. Так вот, лучше будет, если они будут «сломаны» единообразно, то есть можно будет придумать единообразное решение для поломки, которое можно будет переиспользовать в других библиотеках, чем если каждая будет сломана по-своему. И ещё это будет лучше, потому что код у всех будет одинаково «неправилен» и можно будет придумать стандартные решения для всех стандартных проблем стандарта… Поэтому я склоняюсь к тому что всё равно плохой стандарт лучше, чем вообще без стандарта.
Ну то есть все проблемы разрушили и в Windows тоже.

Поздновато, конечно (я бы вот сегодня побоялся выпускать в свет программу, которая Windows 10 1903+ требует), но… лучше поздно, чем никогда!
UTF-8 обратно совместим со старым кодом и заведомо помещается в char: «The character types are large enough to represent any UTF-8 eight-bit code unit (since C++14)». Для экзотических кодировок можно использовать std::codecvt, но зачем, если уже даже в Windows завезли UTF-8?
В C++20 появился char8_t. Интересно, в каких ситуациях следует применять его, а не char?
Если вкратце — во всех возможных ситуациях. Он не alias-ится со всем подряд, из-за чего использование char8_t порождает более производительный код.

Жаль только что он ещё мало где поддерживается стандартными библиотеками

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

Приведите пожалуйста примеры
Вот, например, пара проблем из реальной жизни:
1) Когда одно и то же место в памяти может быть переиспользовано для хранения данных разных типов, а сам тип определяется, исходя из значения какого-то битового поля. Для того, чтобы это корректно работало, приходится делать юнион из соответствующих типов и массива char[], при этом установка битового поля происходит естественным образом через присваивание элемента юниона, а проверку всегда приходится делать через массив char[].
2) Если у меня есть логическая организация памяти в виде, скажем 64-разрядных слов (такой формат данных), а в этих словах могут храниться данные меньших размеров, то единственный корректный способ их извлечь/записать — это через битовые операции с 64-разрядными словами, напрямую работать с этими данными по указателю нельзя никоим образом.
Может, эти примеры и достаточно специфические, но при работе с низкоуровневыми форматами данных подобные проблемы встречаются с завидной регулярностью.
P.S. Я не призываю убрать strict aliasing, в большинстве случаев это действительно полезное ограничение, но можно было подумать и о тех случаях когда он создает лишний геморрой. Можно же, наверное, сделать атрибут [[may_alias]] или что-нибудь в этом роде…
Его, в сущности, только легализовать нужно. В компиляторах поддержка уже есть. Да и даже если нет то почти должна быть: ведь как-то же char/unsigned char/std::byte им нужно «особым образом» обрабатывать?

Так что можете писать proposal.
Правда тут есть вот ещё какая тонкость: какой вообще процент нужд не закрываются std::bit_cast и требуют [[may_alias]]?

Потому что разборка с тегами прекрасно работает через bit_cast.

Я бы, перед написанием пропозала, об этом вначале подумал.
Во-первых, bit_cast только появился в стандарте.
Во-вторых, bit_cast осуществляет преобразование между типами, создавая копию, он не позволяет работать непосредственно с памятью.
А так, естественно, код в приведенном примере будет работать корректно.
Во-первых, bit_cast только появился в стандарте.
Это был бы хороший довод, если бы may_alias там был бы раньше — но его там пока что нет вообще. Ручками bit_cast делается в C++98, там даже фич C++11 не нужно — единственно, что в отличие от «официального» std::bit_cast он в constexpr функциях не работает.

А [[may_alias]] в стандарте нету и ручками он не делается. Так что если его и вводить — то хорошо бы увидеть пример, где недостаточно bit_cast.

Во-вторых, bit_cast осуществляет преобразование между типами, создавая копию, он не позволяет работать непосредственно с памятью.
И это его главное преимущество. «Непосредственная работа с памятью» — это то, чего всеми способами нужно избегать. Ибо она очень плохо оптимизируется.

Лишние копии объектов компиляторы умеют изводить очень хорошо. А вот оптимизировать доступ в память, когда у них нет полной картины того, что происходит — наоборот, с этим всё очень плохо.
Иногда без работы с памятью никак. Что Вы будете делать, если у Вас, например, в структурах данных, которые могут алиаситься, присутствуют атомарные поля?
На самом деле, спор ни о чем — есть много случаев, когда алиасинг вреден и его можно и нужно избегать, есть и обратные примеры.
На самом деле, спор ни о чем — есть много случаев, когда алиасинг вреден и его можно и нужно избегать, есть и обратные примеры.
Разговор как раз именно такой, какой будет в комитете по стандартизации. Не «хацю вот такую фицю», а «хочу то-то и то-то, потому что это нужно для того-то и сего-то».

Что Вы будете делать, если у Вас, например, в структурах данных, которые могут алиаситься, присутствуют атомарные поля?
Попрошу архитектора этого творения сходить обследоваться у психиатра?

Какую бизнес-задачу вы решаете? Зачем вам это понадобилось?

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

Ну так в этом и смысл: перед тем, как менять спецификации языка хочется всегда понять — а нельзя ли «малой кровью» что-то в этих шагах исправить?

Ваш пример я понял — да, это классический NaN-боксинг. Понятно — где, что, как и почему. Понятно что std::bit_cast решает.

Ok, вы говорите, что у вас структуры с алисингом атомарных полей. Причём таких, что std::bit_cast не поможет.

Идея интересная… но зачем? Какая была изначальная задача? Что именно за проблему мы решаем? Как часто она возникает (если она возникает один раз на миллион строк кода всегда можно вкурить ассемблерную вставку и не париться).

А вы все эти вопросы «замять» хотите…
Представьте, например, какую-нибудь сложную lock-free структуру данных… ну, например, дерево, у которого есть разные типы узлов, тип определяется битовым тегом, а дочерние узлы лежат по атомарным указателям… Это только то, что сразу в голову приходит.
Я бы спросил немного про конкретику. Тот факт, что поле у вас является атомарным — совершенно не значит, что его нельзя засунуть в bit_cast.

То что вы описываете — это что-то совершенно фантастическое: у вас по одному адресу лежат два разных типа данных и оба атомарные и доступ к обоим должен быть атомарным… какая-то жуткая химера…

Я не говорю что там не бывает никогда — я говорю что хотелось бы увидеть пример. Как весь алгоритм устроен? Куда и какие барьеры вы пописываете? Почему там нельзя использовать один тим? Ну как-то уж очень много странных условий вы в одно место стащили… я даже не могу представить что должно на выходе (в машинных кодах) получиться!
Интересно, в каких ситуациях следует применять его, а не char?

По идее для любой работы с utf-8 строками где раньше использовался char.
Еще меньше проблем с "aliasing".

> Комитет по стандартизации C++: Концепты, Корутины, Модули, Internal linkage, Module linkage, Ranges, Constexpr-алгоритмы, noexcept, Контракты, Executors

И по традиции — они опять забыли про лаяй-генераторы:

Члены комитета по стандартизации ISO/IEC C++ выступили с критикой нового стандарта языка

Когда уже у нас будут хотя бы рекуррентные конструкторы?

Что-то по ключевым словам "лаяй-генераторы" гуглятся только перепечатки одного и того же текста. Похоже на какой-то троллинг. Зачем писать его рядом с нужными вещами, которые должны были быть включены в язык ещё 10 лет назад?

По ссылке прямо написано: Раздел Юмор

То есть, вас не смутили концепторы, каппа-функторы, рекуррентные конструкторы и прочий бред типа REALLYNULL? Вы назвали все это важными вещами, и претензия у вас только к лаяй-генераторам :) Блин, это очень смешно.

По секрету: весь текст написан просто путем состыковывания случайных computer science терминов. Но вы ухитрились в нем увидеть важные вещи. Плюсовики такие плюсовики :) Теперь я понимаю, почему эту новость выпилили даже с ЛОР-а на первое апреля.

Спасибо вам за хорошее настроение!

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


Но важными вещами я назвал не их, а "Концепты, Корутины, Модули, Internal linkage, Module linkage, Ranges, Constexpr-алгоритмы, noexcept, Контракты, Executors" из вашего комментария выше.

А что с json/csv/xml? Какие то идеи по поводу сариализаций в них обсуждаются, или все же стоит ждать рефлексию в C++23, а потом в лучшем случае еще года три?

Без рефлексии нормальную сериализацию можно сделать только внешним кодогенератором, а внешнему кодогенератору достаточно и существующего стандарта.


Так что, я думаю что именно так и будет. Сначала рефлексия, потом ещё года три-шесть.

Нативной поддержки этих форматов ждать пока что уж точно не стоит. Рефлексия важнее
Для меня, самым серьезным прорывом в С++ были:
1. Работа с файловой системой.
2. Работа с многопоточностью.
3. Работа в функциональном стиле.

Я ожидал, что наконец добавят поддержку сетевой работы, интегрируют asio в стандартную библиотеку. Из этих мелких фундаментальных инстурментов, понравился rvalue, не хватало его для оптимизации логики. Видимо сетевая работа в 2020 неактуальна для C++, спасибо хоть многопоточность есть из коробки. Подождем до 2030, может добавят.
За то время, что Networking варится в комитете, он стал ощутимо лучше (нормальная поддержка аллокаторов, хорошая реализация одинарного буффера...)

Concepts позволяют сделать Networking ещё лучше. Одна из причин отсутствия Networking в C++20 — не успели доработать для него концепты

Есть много старых классических учебников для начинающих по С++, но я чтото не могу найти учебника для изучения последних версий C++17/20 с нуля, а не с изучения старых версий а потом изучения изменений и улучшений. Может кто нибудь мне подскажет самоучитель на русском? Ещё бы хотелось для студентов изучающих первый язык программирования какой нибудь portable компилятор C++17/20 с лёгкой IDE? который студенты могли бы сразу поставить на Win7/10 и попробовать писать простые программы...

Может кто нибудь мне подскажет самоучитель на русском?

Специализация от Яндекса на Курсере: ru.coursera.org/specializations/c-plus-plus-modern-development
Именно современный C++, без древностей.

Попробуйте среду Code::Blocks (http://www.codeblocks.org/).
ну выучишь ты С++17/20 — толку если не все их еще поддерживают? или встретишь tuple какой-нибудь, который в 11/14 появился… Лучше учить последовательно — тем более ничего не выпилили из того что добавляли.

Почему стандарт продолжает делать вид, что юникод ещё не изобрели? Почему std::string хранит байты, и часто программисты его использует просто под бинарные буфера. Я хочу работать с юникодом в std::string, чтобы отделять байты и буквы как в python3, хочу из коробки, как в каком-нибудь Qt.

Если у вас есть богатый опыт в работе с юникодом и какие-то идеи, то пишите proposal или просто сходите в подгруппу SG16: Unicode. Если есть какие-то замечания к существующим proposal — напишите мне, посмотрим что можно сделать
Да нет у него опыта. 99% ноющих про отсутствие поддержки юникода вообще не в курсе того, как устроена работа с многими языками.

Им хочется чтобы русский (тайский, вьетнамский, нужное подчеркнуть) язык «из коробки» поддерживался — и всё.
Я хочу работать с юникодом в std::string, чтобы отделять байты и буквы как в python3
Вот уж чего не нужно — так это «ужаса летящего на крыльях ночи», как в Python3. Где в результате даже имя файла — это не строка.

хочу из коробки, как в каком-нибудь Qt.
Ну дык напишите proposal — а вам его раскатают. И может, лет за 5-10, вы и сделаете что-то, что всех устроит.

Этот вопрос поднимается регулярно и регулярно же получает один и тот же ответ.

Вот тут это уже обсуждали.
UFO just landed and posted this here
Ну можно два строковых типа сделать Ansi и Wide, как в Дельфи уже 10 лет назад сделали.

И как это поможет?
Юникод намного обширнее и хуже, чем "шестнадцатибитный wchar" UCS-2 и UTF-16, к которому многих приучили в Microsoft. И он даже довольно редко так используется, почти везде данные передаются в UTF-8.

Это да, промахнулись в Дельфи в своём время, надо было UTF-8 вводить. Если его ввести как Wide, то уже будет облегчение.
> А я не хочу, потому что это медленно, и платить за unicode-aware-разбор строк при работе с голым ASCII мне бы не хотелось.

То есть, вы не видите разницу между строкой и массивом байт?

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

Эта историческая несуразность, привнесенная из Си, где массив и строка — это одно и тоже, достало уже всех. Думали, что в std::string решат проблему, так нет же, находятся люди, которым only ASCII подавай. Ждем, когда это поколение таки уйдет со сцены.
Ждем, когда это поколение таки уйдет со сцены.
Не дождётесь. Я знаю людей, которым и 20 лет нет — и которые, тем не менее, не разделяют вашей религии:
Строки должны быть строками.

Я вижу последствия «веры в строки» каждый день в Python 3 и вижу сколько боли это всё доставляет там. Причём в Python 2 проблемы были — но их было меньше и решать их было проще.

Потому что на каждую операцию где мне нужно что-то сделать со строкой такое, что мне важно, что там внутри Unicode — находится десяток, а то и сотня операций, где подход «строка — это массив байт» работает лучше и быстрее.

P.S. Заметьте что, скажем, в Go — строки это тоже последовательность байт и это никого особо не напрягает. А язык новый и имеет в стандартой библиотеке и JSON и HTTP и кучу всего ещё.
P.S. Заметьте что, скажем, в Go — строки это тоже последовательность байт и это никого особо не напрягает.

Кое-кого всё же напрягает

Из всего длинной (и довольно забавной, местами ошибочной) статьи проблемы со стороками упоминаются ровно в одном месте — и то в таком контексте, что, дескать в Go, из-за того, строки — это всего лишь последовательность байт — можно напечатать строку, содержащую нечитаемое имя файла. А в Rust, этого, типа, сделать нельзя.

И поэтому вы хотите задащить Unicode в C++? В язык, где всё-всё-всё безопасно и надёжно и важно защититься от ошибок?

Что вы такое курите и почему не делитесь? C++ всегда стоял на принципах: «программист знает лучше». Там нет «подушек безопасности». Хорошо или плохо это — другой вопрос… но таковы «правила игры».

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

Устраивать карго культ и тащить в C++ всякие примочки из других языков даже не задумываясь — зачем они там и чего они этим стремятся добиться… последнее дело.

Уж извините.
C++ всегда стоял на принципах: «программист знает лучше». Там нет «подушек безопасности». Хорошо или плохо это — другой вопрос…

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