C++20 утверждён! Чего ждать и к чему готовиться разработчикам в C++23 +67





На днях в Праге прошла встреча международного комитета по стандартизации C++. И-и-и-и…



C++20 готов! Осталось поставить штампик от ISO, но это чисто формальный шаг, с которым не должно быть проблем.

Поздравляю всех с этим замечательным событием! Concepts, Coroutines, Modules, Ranges, std::format, constexpr new и constexpr алгоритмы+vector+string, datetime, jthread, span, bit_cast и многие другие мелкие и большие нововведения.

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

Приёмы из C в C++20


В последнее время сложилась традиция пугать начинающих разработчиков неопределённым поведением (UB) в C++. Пришло время это изменить!

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

struct X { int a, b; };

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

Но в C++ с ним были большие проблемы. C оперирует байтами, а C++ работает с объектами. А у объекта есть время жизни, и до C++20 начало жизни у объекта считалось от вызова new.

Комитет серьёзно озаботился низкоуровневой работой с байтами и простыми структурами. Приняли улучшения, которые говорят, что определённый набор функций (memcpy, memmove, malloc, aligned_alloc, calloc, realloc, bit_cast) начинает время жизни объекта. Теперь большая часть низкоуровневых C-трюков гарантированно работает в C++.

bool стал надёжнее в C++20


Угадайте, в чём ошибка:

template <typename T, size_t N>
auto count_unique(const std::array<T, N>& v) {
    return std::unordered_set<T>{v.begin(), v.end()}.size();
}

Ответ
Если T является bool и в вашей имплементации стандартной библиотеки v.begin() и v.end() возвращают указатели (например, вы пользуетесь libc++ или libstdc++) — то функция count_unique будет возвращать 1.

Всё из-за того, что в std::unordered_set<bool>{v.begin(), v.end()} указатели будут неявно преобразованы к bool и вы получите выражение std::unordered_set<bool>{true, true}.

Преобразования в bool теперь считаются сужающими преобразованиями. Это позволило найти проблемы во многих кодовых базах (больше примеров в самом предложении P1957).

C++20 концепты стали быстрее


До недавнего времени вы могли написать концепт наподобие такого:

template <class T>
concept Reservable = requires(T v) {
    v.reserve(int{});
};

И удивляться тому, что он возвращает разные результаты:

struct Test;

static_assert(!Reservable<Test>);

struct Test {
    void reserve(int);
};

static_assert(Reservable<Test>);

В прошлый раз мы от страны отправляли комментарий: «Сделайте так, чтобы в концептах нельзя было использовать incomplete type, потому что иначе вы получаете множественные нарушения ODR». Наш комментарий отклонили, но мы частично получили нужный результат сейчас с предложением P2104.

Бонусом получаем более быструю компиляцию, так как компиляторы отныне вправе кэшировать результаты применения концептов к типам.

Мелкие правки в C++20


  • Ranges обзавелись методом ssize.
  • Internal linkage сущности более не видны при инстанцировании module linkage сущностей (компилятор на этапе компиляции вам подскажет, что не так).
  • Подкрутили правила для модулей, чтобы всяким тулзам было проще с ними работать.

А давайте всё сломаем в C++23 или C++26?


Длительные дебаты вызвало предложение об Application Binary Interface (ABI, не путайте с API). Были подняты интересные вопросы:

1. Мы можем полностью сменить ABI в C++23 и получить 5-10% прироста производительности.


При этом все старые C++ библиотеки придётся пересобрать, они не смогут линковаться с библиотеками с новым ABI. Вы не сможете воспользоваться в C++23 проекте библиотеками, собранными более ранними версиями C++.

Ну и разумеется, всегда найдётся старое коммерческое ПО, которое уже никто пересобирать не станет, но которое будет тащить свою стандартную библиотеку (да-да, видеоигрушки, я про вас!).

С небольшим перевесом голосов решили ABI в C++23 не ломать.

2. Давайте дадим пользователям гарантию, что мы будем стараться не ломать/менять ABI.


И тут решили гарантию не давать. Разные вендоры имеют различные планы на свои платформы, и порой они могут позволить себе сломать ABI, зачастую без вреда для пользователей.

А давайте добавим везде noexcept?


Исторически так сложилось, что в стандарте функции с предусловиями не помечаются как noexcept, даже если они никогда не кидают исключения. Вот, например, operator -> у std::optional:

constexpr const T* operator->() const;
constexpr T* operator->();
    Requires: *this contains a value.
    Returns: val.
    Throws: Nothing.

Он ничего не кидает, однако вместо noexcept словами написано что «Кидает: Ничего», потому что есть предусловие «*this содержит значение».

Пользователям c noexcept будет понятнее. Хорошая же идея в P1656!



Нет!

Есть целая подгруппа SG21: Contracts, которая придумывает общий механизм для проверки контрактов (пред- и постусловий). Обработчики контракта могут кидать исключение, если исключение кинуть из noexcept функции — будет std::terminate и приложение рухнет. Если же вставить особый костыль, что для контрактов исключения могут вылетать из noexcept функции… То всё равно всё ломается, type traits ориентируются на наличие noexcept, и начнут вам врать, помеченная noexcept функция с предусловием будет кидать исключение.

Но это не самая большая проблема. Есть форки стандартных библиотек, которые уже сейчас в ряде случаев явно вставляют проверки предусловий. Есть у вас, например, критически важный проект, доступность которого должна быть максимальной. Вы используете подобный форк, и если вдруг кто-то позвал std::vector::back() для пустого вектора — то вылетает исключение, которое обрабатывается выше по коду и начинает использоваться fallback. С правками из P1656 такая библиотека больше не может считаться стандартной.

И это ещё не все проблемы!.. Мало того что дополнительные noexcept для стандартной библиотеки не принесут никаких положительных эффектов в виде уменьшения размера бинарных файлов или большей производительности, мало того что изменение ломает код как минимум двух компаний, мало того что уничтожает один из способов использования контрактов… так ещё и предложение было одобрено уже в двух подгруппах.

Заслуги РГ21


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

Одна из выдающихся идей, которые нам посчастливилось представлять, — это идея Антона Жилина P2025 Guaranteed copy elision for named return objects. Её внедрение позволит создавать функции фабрики для объектов без copy и move конструкторов. Фактически это destructive move, который тайно существовал в стандарте с середины 90-х и был специально запрещён отдельными правилами языка.

Идею мы успешно протащили через инстанции EWG-I и EWG благодаря отличной проработке идеи самим автором. Остался этап CWG, и через пару заседаний есть все шансы увидеть в стандарте нужные слова, а в компиляторах — первые реализации.

Помимо этой идеи мы протащили идею P1990R0: Add operator[] to std::initializer_list через LEWG-I, получили полезный фидбэк на P1944R0: constexpr <cstring> and <cwchar>. Обе идеи Даниила Гончарова имеют все шансы оказаться в C++23.

На поприще std::hash нас ждал неожиданный провал. Обсуждение p1406r1: Add more std::hash specializations внезапно превратилось в обсуждение вырожденных граничных случаев и возможностей далёкого C++2*. В итоге комитет решил ничего не менять.

С SG6 и Numbers не срослось. Основные обсуждения SG6 пересеклись с обсуждениями ABI, из-за чего не набрался кворум в SG6. Из-за этого p1889: C++ Numerics Work In Progress, P2010: Remove iostream operators from P1889 и P1890: C++ Numerics Work In Progress Issues не обсуждались.

Планы на C++23


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

Для C++23 такой план как раз утвердили в Праге. Основные приоритеты C++23:

  1. Поддержка корутин в стандартной библиотеке
  2. Перевести стандартную библиотеку на модули
  3. Executors
  4. Networking

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

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

А вот Executors — проблема. Их дизайн не очевиден, покрывает не все юз-кейсы, в текущем виде они никем не использовались, и дизайн всё ещё не утверждён.

Также комитет согласился приоритизировать предложения по направлениям:

  • Reflection
  • Pattern matching
  • Contracts

Вместо итогов


C++20 готов, пора работать над C++23! Ближайшая встреча комитета будет летом, так что, если у вас есть достойные идеи для нового стандарта — делитесь ими на stdcpp.ru и в Telegram-чате ProCxx.

Ну а все желающие пообщаться с представителями комитета вживую — заглядывайте на митапы и C++ конференции*:


* Лайфхак: за билет на конференцию не надо платить, если ты докладчик.

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

Нужно ли ломать ABI в C++?

  • 74,2%Да385
  • 25,8%Нет134

Помечать ли функции с предусловиями как noexcept, если они «Throws: Nothing»

  • 66,0%Да237
  • 34,0%Нет122

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (313):

  1. NeoCode
    /#21295312

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

    • antoshkka
      /#21295320

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

      • NeoCode
        /#21297202

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

        • khim
          /#21297232

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

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

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

        • antoshkka
          /#21297898

          Да, участвуют. Все предложения проходят через них, без их олобрения предложение принято не будет

      • babylon
        /#21298442

        Вопросы (они же предложения):


        1. Есть ли описание языка в antlr, но представленное в формате json?
        2. Будут ли введены 3-символьные сокращения для токенов?

        • khim
          /#21298746 / +2

          Ответы (довольно очевидные, в общем):
          1. Описание языка в antlr или, тем более, json, принципиально невозможно, так как парсер языка C++ обязан включать в себя интерпретатор достаточно большого подмножества языка C++ (неожиданно, но факт).
          2. Какие 3-символьные сокращения вам вдруг потребовались и зачем?

          • sergegers
            /#21299300 / +1

            3. Триграфы, видимо -).

            • rogoz
              /#21299390

              while -> whi ))

              • antoshkka
                /#21299410 / +3

                Вас ассемблер покусал? ))

                • babylon
                  /#21304286

                  docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#tokens


                  Вопрос конечно больше к Микрософт. Почему нет такого для С++?
                  Эти спеки из чего-то культурно сгенерированы. И полагаю, что не из XML. Хотя возможно.

                  • khim
                    /#21304402

                    Вопрос конечно больше к Микрософт. Почему нет такого для С++?
                    А… зачем? Что в с этим собрались делать и как вам наличие описания, которое не позволяет распарсить код поможет?

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

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

        • Endeavour
          /#21299618 / +2

          ANTLR грамматика в формате JSON? Это вообще как?

  2. ramzes2
    /#21295526

    Нет ли планов на будущее добавить в стандарт контейнер для работы с матрицами, наподобие библиотеки numpy в python? А то уже С++20, а двухмерный массив нужно делать или в C-стиле, или vector<vector>.

    • MyGodIsHe
      /#21295566

      Зачем? numpy внешняя либа, пусть и в крестах так будет.

      • ramzes2
        /#21295636

        Может не совсем аналог numpy, но контейнера для работы с многомерными массивами не хватает. В boost есть ndarray, но boost не всегда можна использовать.

    • antoshkka
      /#21295622

      Сейчас идёт работа над включением в стандарт BLAS и LAPACK. Будут вектора и матрицы, со всеми операциями над ними.

      • Dave_by
        /#21297250

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

        • khim
          /#21297702

          Дык aligned_alloc и std::aligned_alloc давно существуют!

          • Dave_by
            /#21301490

            Это немного не то. Может быть требование чтобы размер каждой строки был выровнен например по 4 байта.

            • khim
              /#21301554

              Для этого есть alignas. Он ещё дольше существует.

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

      • perfhunter
        /#21300484

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

  3. xFFFF
    /#21295590

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

    • antoshkka
      /#21295632

      Но он не будет линковаться с новым кодом. То есть вы будете вынуждены пересобрать legacy-код с новым компилятором, или, если пересобрать нет возможности, вы на всегда останетесь со старой версией стандарта.

      • lovefst
        /#21296068

        В C++17 ещё далеко не все и всё разжевали и прочувствовали.
        Так что когда-то АБИ придётся перекраивать, пусть это будет фишкой 23х плюсов — до его внедрения на практике ещё далеко. К тому же изменений не так уж и много в 20м — скорее чистка стандарта, возможно подготовка к «глобальным» изменениям в 23м… ИМХО.

        • antoshkka
          /#21296082

          Хм, если Concepts, Coroutines, Modules, Ranges для вас небольшие изменения, то что же является большими?

          • VioletGiraffe
            /#21298474

            Ranges — просто библиотека, хоть и классная, могла бы появиться ещё в 11. Поправьте, если ошибаюсь.
            Concepts — отлично для шаблонного кода, но не позволяет делаеть ничего нового, чего нельзя было бы раньше. Тоже поправьте, если что.
            Модули — поверю, когда увижу, что подключение новых 3rdparty библиотек перестало быть болью в голове и заднице :)
            Корутины — прикольно, но специфично, запутанная фича для написания запутанного кода с неочевидным control flow. И, помню, в предыдущих TS недоставало реализации каких-то классов, когда компиляторы уже поддерживали корутины. В С++20 всё нужное будет из коробки?

            • apro
              /#21298500

              поверю, когда увижу, что подключение новых 3rdparty библиотек перестало быть болью в голове и заднице

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

              • VioletGiraffe
                /#21298510 / +2

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

                • apro
                  /#21298574 / +1

                  Разве это не сделает библиотеки более модульными
                  удобными в использовании?

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


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


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


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

                  • VioletGiraffe
                    /#21298698

                    Проблема не в системе сборки как таковой, мне не трудно скопом добавить все исходники из /src/ в свою систему сборки. Источник проблем — запутанная, сложная, непонятная конфигурация с кучей макросов и/или условным включением файлов.
                    Так что да, мой выбор №1 — header-only библиотека, а №2 — отказ от библиотеки с нетривиальной сборкой.

                • DistortNeo
                  /#21299774

                  Разве это не сделает библиотеки более модульными, а значит, удобными в использовании?

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


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

              • 0xd34df00d
                /#21298606 / +3

                Когда я последний раз смотрел, модули никак не помогали ускорить компиляцию, по крайней мере, для кода с темплейтами.

                • sergegers
                  /#21299326

                  Это личный опыт? А какие сборки тестировались? Инкрементальные, полные, unity билды?

                  • 0xd34df00d
                    /#21299354 / +1

                    Это размышления над тем, что дают модули, после чтения пропозала ATOM.


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


                    А любые оптимизации, применимые к модулям, применимы и к инклюдам/pch.


                    Собственно, про это всё писали ещё то ли Страуструп, то ли Саттер, то ли чуваки из EDG лет 18 назад.

                    • sergegers
                      /#21299660

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

            • antoshkka
              /#21298786 / -1

              Ranges и datetime — добавлили отличные библиотеки для рантайма
              Концепты — добавили для метапрограммирования и не только
              Модули — ускоряют компиляцию
              Корутины — позволяют использлвать нечто абсолютно новое
              Constexpr всякое — позволяют compile time вычисления делать с обычным синтаксисом
              Новые правила для тривиальных типов — ускорение рантайм кода


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

              • VioletGiraffe
                /#21299460

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

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

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

                • khim
                  /#21299582

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

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

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

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

                  Тут нужна какая-нибудь популярная библиотека на корутинах (как STL в своё время).

                  • 0xd34df00d
                    /#21299676

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

                    А вы оптимист!

                    • khim
                      /#21299842

                      Нет — я реалист. Сегодня поддержка IPv6 — примерно у 30%. Порог 50% — будет где-то через 3-4 года. После пересечения границы 50% можно спокойно перестать в новых проектах IPv4.

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

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

                      Плюс, опять-таки многое зависит от API: если API будет достаточно высокоуровневым, то факт существования и использования IPv4 может оказаться скрфтым от приложения.

                      • Antervis
                        /#21299900

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

                        • khim
                          /#21299968

                          Нужно появление 1 (одного) популярного приложения, разработчиков которого задолбает борьба с IPv4. После чего всё случится очень быстро.

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

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

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

                  • creker
                    /#21299758

                    придётся поддерживать IPv4

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

                    • khim
                      /#21299846

                      Доживём, не бойтесь. Процесс перехода идёт, хоть и с некоторым отставанием от плана.

                      • creker
                        /#21302246

                        Только это не изменит того факта, что ipv4 никуда не денется. Банально не нужно ipv6 поднимать везде и всюду. Не только интернетом единым все живет.

                        • khim
                          /#21302290 / -2

                          Ну да, так же и про IPX говорили четверть века назад. Вот один-в-один те же слова.

                  • Antervis
                    /#21299780 / +1

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

                    • khim
                      /#21299848

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

                  • perfhunter
                    /#21300642

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

                  • mayorovp
                    /#21302352

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


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

                    • khim
                      /#21302564

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

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

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

                      • mayorovp
                        /#21302590

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


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

                        • khim
                          /#21302728 / -1

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

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

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

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

                          Так что скорее всего будет очередной дико неэффективный монстр в духе … а жаль.

                          • mayorovp
                            /#21302810

                            И не нужно: много у нас систем с поддержкой AF_UNIX, но без поддержки IPv6 остались?

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

                            • khim
                              /#21302946 / -1

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

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

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

                              • antoshkka
                                /#21302980 / +1

                                Вы наверное что-то путаете. AF_UNIX вам может пригодиться вне зависимости от поддержки IPv6, хотя бы потому, что он производительнее чем использование TCP/IP стека

                              • alexac
                                /#21303466

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

                                Для любого взаимодействия внутри машины AF_UNIX будет предпочтительнее, чем AF_INET6/AF_INET. Это просто протоколы с разными целями.

                • 0xd34df00d
                  /#21299674

                  Корутины — это офигенно, на них монадки можно сделать.

            • Antervis
              /#21299750 / +1

              Concepts — отлично для шаблонного кода, но не позволяет делаеть ничего нового, чего нельзя было бы раньше. Тоже поправьте, если что.
              да, можно, но среднему с++ программисту сейчас нужна дока чтобы написать простейший ниблоид.
              Корутины — прикольно, но специфично, запутанная фича для написания запутанного кода с неочевидным control flow
              киллер фича корутин — их control flow прямолинеен, очевиднее уже попросту некуда

              • VioletGiraffe
                /#21299906

                Зачем среднему программисту писать ниблоид? Сколько всякой constexpr фигни на шаблонах понаписывал за последние года три — ни разу с такой задачей не столкнулся. Я, конечно же, не обобщаю свой опыт на всех программистов С++, но проблема кажется очень синтетической. Для чего лично мне нужны концепты — замена уродливых enable_if и более осмысленные сообщения об ошибках.

      • rkfg
        /#21298080

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

        • khim
          /#21298244 / +2

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

          Сейчас в линуксе есть 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.
          Это вам хорошо рассказывать такие сказки если у вас в проекте только десяток человек и всё в одной организации. А если у вас сотни компонент от десятков вендоров, то переход растягивается на годы.

          • rkfg
            /#21298304

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

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


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

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


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

            • khim
              /#21298542 / +1

              Так можно и про 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му году — комитет не захотел.

        • antoshkka
          /#21298366

          Там проблема глубже. На libstdc++.so.6 + libstdc++.so.7 проблемы только начинаются: вам нужно собрать отдельный Boost для libstdc++.so.6 и отдельный для libstdc++.so.7… фактически каждая популярная C++ библиотека удвоится.

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

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

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

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

          • rkfg
            /#21298396

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

            • zanac
              /#21303788

              мост между ABI

              extern «C»?

          • perfhunter
            /#21300688

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

      • perfhunter
        /#21300492

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

    • nmrulin
      /#21299866

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

  4. B0R15
    /#21295608

    Кто может объяснить о каких конкретно проблемах идет речь в этом примере

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

    • antoshkka
      /#21295644

      До C++20 стурктура по адресу p не создавалась. Компилятор мог делать странные оптимизации, подразумевая что по p нет объекта

      • CaptainFlint
        /#21295972

        А есть ли случаи такого поведения в дикой природе, или этот UB был полностью «бумажным»?

        • antoshkka
          /#21296064 / +2

          Большинство известных мне компиляторов так не чудили

          Теперь все обязаны не чудить

          • hobogene
            /#21296886

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

            Upd: возможного чудения, конечно.

            • Overlordff
              /#21296982

              Фрагментация жёсткого диска

              • hobogene
                /#21297156

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

                Upd: хотя фрагментация, конечно, это не форматирование, которое в качестве страшилки про UB детям рассказывается :-)

                • 0xd34df00d
                  /#21298532

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

                • VioletGiraffe
                  /#21298594 / +1

                  Насколько я понимаю, обращения с р как со структурой — чтение и запись полей — невалидны, потому что эта структура никогда не была сконструирована (не вызвался конструктор, the lifetime of an object has not started). Поэтому такие обращения невалидны, и если компилятор может быть уверен, что конструирования не было, он вправе выкинуть эти обращения вообще, или предположить, что значения полей имеют константное значение, удобное для оптимизации, или предположить, что этот код вообще никогда не может быть фактически вызван, или сгенерировать UD2… Вариантов много, и они вызывают разное непонятное и непредсказуемое поведение.

            • NBAH79
              /#21297136

              Пример определенно странный. Может в примере не new потому что хотят выделить одним блоком место под несколько структур за раз. Почему то же вручную задают размер… Или можно предположить, что косяк происходит с алигнментом и некорректным расчетом длинны структуры в каких то редких компиляторах, в результате чего хвост используемой в последствии структуры вылезет за пределы выделенной памяти из-за паддинга элементов структуры. Както так… Я на Шарпе просто уже года три, уже забыл про эти проблемы, могу ошибаться.
              З.Ы. Мне кажется им надо не из парохода делать паровоз, а просто создать новый с+++ с новым ABI и его мучить. Кому надо-тот легко переведет программу.

              • hobogene
                /#21297210

                Мне кажется им надо не из парохода делать паровоз,


                А чем тогда будут заниматься люди с фотографии?

              • khim
                /#21297218

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

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

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

                • hobogene
                  /#21297272

                  Delphi продается далеко не так, как продавался. А Хейлсберг стоял за C#, который продается гораздо лучше.

                  • khim
                    /#21297482

                    Delphi продается далеко не так, как продавался.
                    Однако же Modula-2 и Oberon продаются гораздо, гораздо хуже.

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

                    • hobogene
                      /#21297568

                      Речь в комментарии, на который я отвечал, шла про

                      просто создать новый с+++
                      .

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

                      • khim
                        /#21297704

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

                        Рассчитывать же на то, что таких идиотов найдётся много — уже и совсем бессмысленно.

                • NBAH79
                  /#21297420

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

                  • khim
                    /#21297490

                    Создайте четкие стантарты ABI для кроссплатформенности и у вас получится лучше чем Шарп ;)
                    Совершенно не факт.

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

            • antoshkka
              /#21297920

              Пример: Структура не создана, значит и запись в неё не имеет смысла и можно её не производить.

              Другой пример: структура не создана, а значит и последующие чтения из неё не имеют смысла и можно заменить на чтение 0.

              • hobogene
                /#21299718

                У нее же нету пресловутой non-vacuous initialization. Выделили storage — началось lifetime. Нет?

            • yrHeTaTeJlb
              /#21297930 / +1

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


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

              • hobogene
                /#21299722

                std::malloc дает определенные гарантии.

      • ishmakov
        /#21297254

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

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

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

        • hobogene
          /#21297284 / -1

          Ну, видимо, да. Про конструкторы-деструкторы-виртуальные функции и из них вытекающее.

          • khim
            /#21297352

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

            Если тип нетривиален, то его так создавать нельзя.

            • ishmakov
              /#21297406 / -2

              Создавая объект, компилятор создает только упорядоченный блок памяти, в котором есть блок памяти, который вы интерпретируете либо как данные структуры, либо интерпретируете как указатели на методы, среди которых для классов/структур есть такое понятие как конструктор и деструктор, которые вызываются при new/delete. При malloc ничего не вызывается, а только выделяется память.

              • hobogene
                /#21297416

                И вот в этом примере и вызывать нечего.

                • ishmakov
                  /#21297584

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

                  • hobogene
                    /#21297604 / +1

                    Ну, бред — это сурово сказано. Но пример со структурой совершенно неудачный. Забывают люди корни, увлекаясь модными фишками :-)

                    • VioletGiraffe
                      /#21298660 / +2

                      Чем неудачный? Этот код нарушает правила языка С++, это не код валидной программы на С++. Пример очень жизненный и правильный, мне всё время приходится держать это в уме и иногда писать более сложный и чуть менее оптимальный код, чтоб не нарушать это правило.

                      • hobogene
                        /#21299762 / -1

                        нарушает правила языка С++


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

                        • khim
                          /#21299862 / +1

                          выделили storage — lifetime пошло.
                          Это с какого перепугу-то?

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

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

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

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

                          Вот тут может быть… неприятность…

                          • hobogene
                            /#21299880

                            Это с какого перепугу-то?


                            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, под рукой прямо сейчас ничего другого нет. Но не думаю, что радикально как-то в не-драфте поменялось.

                            • Antervis
                              /#21299902

                              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
                              лайфтайм начинается когда для объекта выделена память и завершилась его инициализация (если таковая этому классу требуется).

                              • hobogene
                                /#21299910

                                Совершенно верно. Структуре из примера требуется?

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

                                Upd2: вот в 20-м драфте уже «its initialization (if any) is complete (including vacuous initialization) „

                                • Antervis
                                  /#21299944

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

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

                                  • hobogene
                                    /#21299984

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


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

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


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

                                    • Antervis
                                      /#21300930

                                      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;
                                      // Хм. Мы же точно не создали объект дважды, верно?
                                      По сути, формально буква стандарта и компиляторы чуть-чуть по разному этот кейс трактовали. Вот и подправили стандарт чтобы не было расхождения «формально»/«на деле».

                          • hobogene
                            /#21299898

                            а стандарт этого не позволяет.

                            Изначальный пост-то про то, что теперь-то 146%, как позволяет, нет?

          • ishmakov
            /#21297354

            Ну в статье об этом не сказано, надо курить все же что имели в виду в стандарте.

        • khim
          /#21297344 / -1

          Ничего не понял
          Потому что не пытались.

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

          О чем вообще идет речь? Что 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

          • hobogene
            /#21297412

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


            Слово storage пропало из стандарта?

            • khim
              /#21297508

              Нет конечно. Где-то же объекты должны существовать. Но вы нигде и никак не можете оперировать с «сырой памятью» — только с объектами.

              • hobogene
                /#21297746 / +1

                raw_storage_iterator и т.п.
                и dynamic_cast к void*.

                • khim
                  /#21297780

                  raw_storage_iterator удалён из C++20 — именно потому что никакого способа использовать его в варидной программе на C++ стандарт не давал.

                  • hobogene
                    /#21298156

                    Текущий вариант мог создавать проблемы. А мог и не создавать. Это не то же самое, что принципиальная невозможность использовать, и вообще работать с raw storage. Проблема, кстати, еще с 11-м стандартом возникла. Что не помешало raw_storage_iterator дожить до 20-го.

                    • khim
                      /#21298256

                      А мог и не создавать.
                      Не мог. Практически любая программа с его оспользованием вызывала UB и, соотвественно, программой на C++ не являлась.

                      Что не помешало raw_storage_iterator дожить до 20-го.
                      Если бы им реально пользовались, то его и в C++20 не выкинули бы.

          • hobogene
            /#21297434

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


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

            • mayorovp
              /#21297470 / +1

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

              • hobogene
                /#21297592

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

                • mayorovp
                  /#21297638

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


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


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

                  • hobogene
                    /#21297686

                    Но я-то писал про конкретно этот :-) Спасибо за ссылки на страшилки, кому-нибудь пригодится.

                  • khim
                    /#21297688

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

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

                    Но я тут не совсем уверен, что hobogene именно это имеет в виду.

                  • VioletGiraffe
                    /#21299914

                    Ооо, какая шикарная подборка настоящих живых УБ, а не обычное «винт тебе форматнёт. А потом брата убьёт.». Спасибо!

                • khim
                  /#21297644

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

                  • hobogene
                    /#21297726

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


                    Вы сами про это написали. Более, того, писали, что это будет именно из-за UB. Я попросил подтвердить примером. Если не вышло, ну и не вышло. Никто не умрет.

                    • 0xd34df00d
                      /#21298552 / +1

                      Это может быть из-за UB. Просто текущие компиляторы недостаточно умны (или недостаточно ненавидят своих пользователей), чтобы этим пользоваться.

                      • khim
                        /#21298768

                        К сожалению (или к счастью) — нет.

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

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

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

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

                        Но при этом почему-то именно за эту фразу hobogene зацепился и теперь носится с ней, как с писаной торбой…

                        • 0xd34df00d
                          /#21298778 / +1

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


                          Но это, конечно, все неважно. ИМХО «функция выкинулась вся» и «от функции остался один лейбл» примерно эквивалентны.

                          • khim
                            /#21298806

                            Но это, конечно, все неважно. ИМХО «функция выкинулась вся» и «от функции остался один лейбл» примерно эквивалентны.
                            Там не лейбл. Там целый jmp остался.

                            • 0xd34df00d
                              /#21298892

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

                              • khim
                                /#21298936

                                Ну вот не любит gcc совсем пустые функции почему-то…

        • mayorovp
          /#21297462

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


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


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

          • khim
            /#21297602

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

            И вот когда с этим попробовали работать — как раз и выяснилось, что куча программ и библиотек к этому нифига не готовы… и, в общем, C++ стандарт отчасти тоже виноват: если на платформе есть intptr_t, то, в соотвествии со стандартом, можно указатель в него преобразовать и обратно тоже. Правда этот тип не является обязательным, так что теоретически стандарт к работе в системе, где всё на свете объекты таки готов.

            • 0xd34df00d
              /#21298564

              и, в общем, C++ стандарт отчасти тоже виноват: если на платформе есть intptr_t, то, в соотвествии со стандартом, можно указатель в него преобразовать и обратно тоже

              Не вижу проблем: ну да, есть биекция между двумя множествами. Но это же даже не изоморфизм. Вы не можете складывать полученные числа, ожидая, что что-то разумное получится (кроме адресации внутри массивов, возможно, но я не уверен).

              • khim
                /#21298798

                Вы не можете складывать полученные числа, ожидая, что что-то разумное получится (кроме адресации внутри массивов, возможно, но я не уверен).
                Зато можете, скажем, записать в файл на диск, а потом прочитать из него.

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

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

                Можно, наверное, предоставить процедуру, которая проверит что имеющееся у вас число, которое не помечено как указатель, на самом деле, указывает на валидный объект, к которому вы имеете право доступа и превратит число в указатель… но представьте себе с какой скоростью при таком подходе будет работать тот же XOR-связный список!

          • hobogene
            /#21297622

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

      • perfhunter
        /#21297956

        Хм… А чем формально вышеописанный код отличается от

        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() делает семантически.

        • mayorovp
          /#21297986 / +2

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

          • perfhunter
            /#21300406

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

            • mayorovp
              /#21300462

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


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

              • perfhunter
                /#21300836

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

                • mayorovp
                  /#21300848

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

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

      • ishmakov
        /#21299472 / +1

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

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

        • antoshkka
          /#21299506

          Да, всё верно. Это теперь принято называть implicit construction, и оно срабатывает если у типа есть тривиальный конструктор и деструктор

  5. TargetSan
    /#21295684

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

    • antoshkka
      /#21296736

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

      • TargetSan
        /#21296906

        Я имею ввиду разделение на 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;
        }

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

        • ZaMaZaN4iK
          /#21296950

          Самое большое, что я видел по хешам, это вот эта бумага: link

      • ZaMaZaN4iK
        /#21296936

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

  6. lamerok
    /#21295756

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

    • antoshkka
      /#21296156

      В самом разгаре. Пока всё неочевидно и кажется что 10 раз поменяется

  7. Antervis
    /#21295914

    мало того что изменение ломает код как минимум двух компаний
    всегда найдется компания, у которой что-то сломается, и которые против изменений ради status quo

    • antoshkka
      /#21296144

      > всегда найдется компания, у которой что-то сломается, и которые против изменений ради status quo

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

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

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

      • Antervis
        /#21296810

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

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

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

        • antoshkka
          /#21296846

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

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

          Кстати, добавьте noexcept к vector::back прям сейчас… и вы не увидите разницы в кодгене для GCC (остальные вендоры ленятся с правильной реализацией исключений)

          • Antervis
            /#21296914

            Если трейт начнёт врать, то вектор возможно что всегда будет копировать. А это безумные замедления, никакие мнимые преимущества от noexcept их не перевесят.
            трейт начнет врать если мы начнем навешивать контракты на мув конструкторы/операторы присваивания. А это скорее странный/нежелательный юзкейс нежели типовой
            Кстати, добавьте noexcept к vector::back прям сейчас…
            vector::back сравнительно хорошо инлайнится, а для более сложных функций разница будет заметной.
            остальные вендоры ленятся с правильной реализацией исключений
            да с исключениями вообще всё очень плохо. И поэтому noexcept так важен

          • Antervis
            /#21297004

            кстати, можно же еще вопрос иначе поставить: может быть разрешить стандартным библиотекам усиливать noexcept'ness гарантии для не кидающих функций? Т.е. пометить набор функций в стандарте как noexcept(/*implementation-defined*/)?

          • 0xd34df00d
            /#21298600 / +1

            А можно сделать этим всем методам conditional noexcept? Собираешь в дебаге/с контрактами/етц — получаешь noexcept(false) методы, иначе noexcept.


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

            • 0xd34df00d
              /#21298666

              Хотя noexcept протекает в abi, и я не знаю сходу, сколько геморроя это даст и насколько с ним можно жить. Не линковать дебаговые сборки с релизными?

  8. staticlab
    /#21296026

    А Microsoft как всегда решил вендорлокнуть:


    Although not specified by the C++20 standard, Microsoft enables its implementation of the C++ Standard Library to be imported as modules. By importing the C++ Standard Library as modules rather than #including it through header files, you can potentially speed up compilation times depending on the size of your project. The library is componentized into the following modules:

    std.regex provides the content of header <regex>
    std.filesystem provides the content of header <filesystem>
    std.memory provides the content of header <memory>
    std.threading provides the contents of headers <atomic>, <condition_variable>, <future>, <mutex>, <shared_mutex>, and <thread>
    std.core provides everything else in the C++ Standard Library

    • antoshkka
      /#21296104

      Используйте это на свой страх и риск. Как будут выглядеть стандартные модули ещё не решено, только зарезервированы имена начинающиеся с std. Если к C++23 комитет решит сделать несколько другие имена модулей, код с вышеозвученными MS модулями немного поломается.

      • rogoz
        /#21296752

        А такие конструкции

        import <iostream>;
        как относятся к стандарту?

        • antoshkka
          /#21296812

          Импортирование C++ хедеров обязано работать в C++20. import <iostream>; — это правильно, безопасно.

          Но не так быстро как import std.iostream;, который появится только в C++23

      • Antervis
        /#21296868

        Если к C++23 комитет решит сделать несколько другие имена модулей, код с вышеозвученными MS модулями немного поломается.
        надеюсь если захотят поменять реализацию аргумент MS а-ля «а мы уже вендорлокнули» проигнорируют?

        • rogoz
          /#21296892

          Ну если вариант MS будет более менее, а остальные вариантов лучше не предложат, то могут принять MS вариант, ничего не мешает.

          • khim
            /#21297124

            Увидим. Хороший будет тест на 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 приводиться к стандарту…

            • Alex_ME
              /#21300556

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

          • Antervis
            /#21297356 / -1

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

            • mayorovp
              /#21297550

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


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

  9. ardazishvili
    /#21296222

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


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


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

    • antoshkka
      /#21296440

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

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

      • AndDav
        /#21296470 / +1

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

        • antoshkka
          /#21296492

          Вошло в C++20. Успели в последний момент :)

  10. bubukerrr
    /#21296246

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

    • antoshkka
      /#21296504

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

    • ZaMaZaN4iK
      /#21296988

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

  11. TheCalligrapher
    /#21297112 / +1

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


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

    • khim
      /#21297144

      Угу. Для C нужно писать:

      typedef struct X { int a, b; } X;
      

      Но так уже почти никто не делает…

    • hobogene
      /#21297146

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

  12. ncr
    /#21297690 / +2

    С небольшим перевесом голосов решили ABI в C++23 не ломать.

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

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

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

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

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

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

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

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

    Печаль.

    • Antervis
      /#21297768

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

      • ncr
        /#21297932

        мы тут вообще-то про с++
        Да вы что, я и не заметил.
        Ок, «интерфейсы с Trivial & Standard Layout типами данных». Так понятнее?

        Завернуть можно всё, вопрос в желании.

        • Antervis
          /#21298046 / +2

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

    • khim
      /#21298186 / +1

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

      Выставлять наружу 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++.

      • apro
        /#21298352 / +1

        А вот в мире 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++ собрал,
        на всякий случай.

        • khim
          /#21298644

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

          То есть не все так однозначно, если хочется быть уверенным в надежности работы программы
          С надёжностью работы вообще проблем нет. То, что вы тут накопали — это хитрые случаи 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). И это всё полностью поддерживается, а не как нам тут вещают… про «недокументированное поведение»…

          • apro
            /#21299456 / +1

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

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

            • khim
              /#21299646

              Именно потому и существует опция -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…

      • ncr
        /#21298504 / +1

        Вы сейчас описали ситауцию в 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++.

        • khim
          /#21298724

          Ну да, потому что там, грубо говоря, на всю экосистему один компилятор и одна библиотека, можно упарываться от души.
          А вот давайте не рассказывать сказок? На обычном 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%…

          • ncr
            /#21299120 / +1

            А вот давайте не рассказывать сказок?
            «Грубо говоря». Они, несомненно, есть, но какова их рыночная доля?

            это про язык у которого есть три способа разбора командной строки
            «А у вас негров линчуют»? Я далёк от мнения, что 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, станет тупо вдвое больше.

            • khim
              /#21299346

              «Грубо говоря». Они, несомненно, есть, но какова их рыночная доля?
              А на Windows — что? Есть какой-то компилятор, у которого рыночная доля сравнима с Microsoft C++???

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

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

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

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

              • ncr
                /#21299542

                Есть какой-то компилятор, у которого рыночная доля сравнима с Microsoft C++???
                Конечно есть — Microsoft C++ предыдущих версий. Каждая со своим рантаймом и своим ABI, вопросами совместимости которого до недавнего времени в MS не заморачивались.

                А про «get out»… поживём, увидим
                Я внезапно осознал, что был в корне неправ, get out не нужен. Больше легаси — сложнее язык — выше порог входа — меньше конкуренция — работа будет всегда. Ура комитету!

                • khim
                  /#21299662

                  Конечно есть — Microsoft C++ предыдущих версий. Каждая со своим рантаймом и своим ABI, вопросами совместимости которого до недавнего времени в MS не заморачивались.
                  Ну то есть вся проблема существовала только и исключительно благодаря Microsoft'у. Причём что самом смешное, для себя-то, любимых, они всё сделали правильно.

    • 0xd34df00d
      /#21299362

      Если вдруг надо вынести код в библиотеку — используем C-интерфейсы между модулями. Всё. Работает с любой версией любого компилятора.

      Вы всерьёз предлагаете оформлять API, скажем, boost.thread или boost.graph в виде сишного API? Я бы с интересом на это посмотрел.

      • ncr
        /#21299566

        Можете посмотреть на CPython. Много писанины, но не rocket science.
        А я бы с интересом посмотрел, зачем вам таскать thread или graph между модулями.

  13. ss-nopol
    /#21297800 / +1

    С моей колокольни полная поддержка юникода важнее всего вышеперечисленного. Будем ждать…

    • vanxant
      /#21297876

      А что, до сих пор не завезли? Ууу блин:(

      • khim
        /#21298200 / +1

        Не завезли и не завезут пока в C++ не появятся опциональные компоненты и эту поддержку нельзя будет объявить опцией.

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

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

        • ss-nopol
          /#21298630

          желающих пользоваться — вагон, желающих работу делать — нуль.

          Почему-то во многих других более молодых языках эту проблему как-то решили. Странно. Тем более странно при вагоне желающих. С сегодняшними успехами краудфандинга не дожно быть проблем и со сбором денег на реализацию. Загадка.

          • 0xd34df00d
            /#21298650

            Я не знаю языков с полной поддержкой уникода. Где её решили?

            • ss-nopol
              /#21298730

              Я не знаю языков с полной поддержкой уникода. Где её решили?

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

              • 0xd34df00d
                /#21298792 / +2

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

              • khim
                /#21298836 / -1

                Однако у большинства всё значительно лучше, чем в C++.
                Я сказал, что у большинства значительно хуже, чем в C++.

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

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

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

                • ss-nopol
                  /#21299006

                  покажите мне язык, где это можно просто сделать не перепутав «I» и «i» в Турецком языке?

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

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

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

                  • khim
                    /#21299276

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

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

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

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

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

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

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

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

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

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

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

                    Стандарт ведь не эльфы разрабатывают…

                    • ss-nopol
                      /#21299776

                      Турки бы тупо отказались бы переходить на Unicode.

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

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

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

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

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

                      • khim
                        /#21299958

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

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

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

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

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

                        • ss-nopol
                          /#21301576

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


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

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

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

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

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

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

                    • agmt
                      /#21300078

                      более-менее на всех популярных, кроме Windows)

                      docs.microsoft.com/en-us/windows/uwp/design/globalizing/use-utf8-code-page

                      • khim
                        /#21301364

                        Ну то есть все проблемы разрушили и в Windows тоже.

                        Поздновато, конечно (я бы вот сегодня побоялся выпускать в свет программу, которая Windows 10 1903+ требует), но… лучше поздно, чем никогда!

    • agmt
      /#21298332 / +1

      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?

      • antoshkka
        /#21298392

        Если вкратце — во всех возможных ситуациях. Он не alias-ится со всем подряд, из-за чего использование char8_t порождает более производительный код.

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

        • perfhunter
          /#21300472

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

          • antoshkka
            /#21300582

            Приведите пожалуйста примеры

            • perfhunter
              /#21301696

              Вот, например, пара проблем из реальной жизни:
              1) Когда одно и то же место в памяти может быть переиспользовано для хранения данных разных типов, а сам тип определяется, исходя из значения какого-то битового поля. Для того, чтобы это корректно работало, приходится делать юнион из соответствующих типов и массива char[], при этом установка битового поля происходит естественным образом через присваивание элемента юниона, а проверку всегда приходится делать через массив char[].
              2) Если у меня есть логическая организация памяти в виде, скажем 64-разрядных слов (такой формат данных), а в этих словах могут храниться данные меньших размеров, то единственный корректный способ их извлечь/записать — это через битовые операции с 64-разрядными словами, напрямую работать с этими данными по указателю нельзя никоим образом.
              Может, эти примеры и достаточно специфические, но при работе с низкоуровневыми форматами данных подобные проблемы встречаются с завидной регулярностью.

              • perfhunter
                /#21301752

                P.S. Я не призываю убрать strict aliasing, в большинстве случаев это действительно полезное ограничение, но можно было подумать и о тех случаях когда он создает лишний геморрой. Можно же, наверное, сделать атрибут [[may_alias]] или что-нибудь в этом роде…

                • khim
                  /#21301852 / +1

                  Его, в сущности, только легализовать нужно. В компиляторах поддержка уже есть. Да и даже если нет то почти должна быть: ведь как-то же char/unsigned char/std::byte им нужно «особым образом» обрабатывать?

                  Так что можете писать proposal.

                • khim
                  /#21302304

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

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

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

                  • perfhunter
                    /#21302918

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

                    • khim
                      /#21302992

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

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

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

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

                      • perfhunter
                        /#21303086

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

                        • khim
                          /#21303212

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

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

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

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

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

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

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

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

                          А вы все эти вопросы «замять» хотите…

                          • perfhunter
                            /#21303240

                            Представьте, например, какую-нибудь сложную lock-free структуру данных… ну, например, дерево, у которого есть разные типы узлов, тип определяется битовым тегом, а дочерние узлы лежат по атомарным указателям… Это только то, что сразу в голову приходит.

                            • khim
                              /#21303620

                              Я бы спросил немного про конкретику. Тот факт, что поле у вас является атомарным — совершенно не значит, что его нельзя засунуть в bit_cast.

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

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

      • apro
        /#21298414

        Интересно, в каких ситуациях следует применять его, а не char?

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

  14. webhamster
    /#21297910 / -1

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

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

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

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

    • mayorovp
      /#21297970

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

      • encyclopedist
        /#21298128 / +1

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

      • webhamster
        /#21301276

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

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

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

        • mayorovp
          /#21301408

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


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

  15. MomoDev
    /#21297944

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

    • mayorovp
      /#21297996

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


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

    • ZaMaZaN4iK
      /#21298468

      Нативной поддержки этих форматов ждать пока что уж точно не стоит. Рефлексия важнее

  16. plain-c
    /#21297978 / +4

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

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

    • antoshkka
      /#21303228

      За то время, что Networking варится в комитете, он стал ощутимо лучше (нормальная поддержка аллокаторов, хорошая реализация одинарного буффера...)

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

  17. wikipro
    /#21298710

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

    • Amikko
      /#21299118 / +1

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

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

      Попробуйте среду Code::Blocks (http://www.codeblocks.org/).

    • std_FoCuS
      /#21299418 / +1

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

  18. Tuxman
    /#21299236 / +1

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

    • antoshkka
      /#21299282

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

      • khim
        /#21299304 / -1

        Да нет у него опыта. 99% ноющих про отсутствие поддержки юникода вообще не в курсе того, как устроена работа с многими языками.

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

    • khim
      /#21299294

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

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

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

      Вот тут это уже обсуждали.

    • 0xd34df00d
      /#21299388

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

      А я не хочу, потому что это медленно, и платить за unicode-aware-разбор строк при работе с голым ASCII мне бы не хотелось.


      Отказываясь от юникода в других языках, даже где оптимизаторы похуже и могут делать меньше всяких прикольных вещей, я систематически получаю профит от 3 до 50 раз. Я не хочу платить эту цену в плюсах, извините.


      Ну и да, отделять буквы… А почему не глифы? А skin tone для эмодзи учитывать как отдельную букву или как?

      • nmrulin
        /#21299870

        Ну можно два строковых типа сделать Ansi и Wide, как в Дельфи уже 10 лет назад сделали.

        • DerRotBaron
          /#21299946

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

          • nmrulin
            /#21305142

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

    • agmt
      /#21299922

      Qt не отделяет даже буквы: https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Multilingual_Plane. UTF-16 внезапно оказалось недостаточно и появились суррогатные пары. К тому же, что делать с эмодзи, которые могут состоять из нескольких юникод-символов?
      Вот даже интересно: какую реальную задачу можно легко решить с UCS-4, но сложно с UTF-8? Длина строки в символах — одинаково сложная задача в обоих кодировках, потому что символы могут быть модификаторами предыдущего, а для графического размера надо отрисовывать, потому что символы могут быть разной ширины.

  19. navrocky
    /#21299790

    А в каком десятилетии extensions к классам завезут? Такая же простая но крайне полезная фича

    • Antervis
      /#21299916

      надеюсь ни в каком, расширения — очень вредная фича. Если её завезти в с++, любой класс внезапно перестанет быть завершенным и контекстуально независимым. А плюсы и так далеко не самый легко читаемый/анализируемый язык.

  20. eao197
    /#21300342

    Антон, подскажите, пожалуйста, а что с предложением по std::expected? Будет std::expected в С++20 или...

  21. static_if
    /#21300590

    extern «Cpp<2b» { /* your ABI dependent code here */ }
    чем не выход?

    • antoshkka
      /#21300666

      Это большое усложнение языка ради небольшого выигрыша. Скорее уж надо пойти по пути введения в стандарте термина, обозначающего «при следующем сломе ABI». Тогда можно говорить «при следующем сломе ABI конструктор копирования tuple становится тривиальным для тривиальных типов»

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

      • Aldrog
        /#21301010

        А что мешает и без такого термина вендорам заявлять экспериментальную поддержку C++23, а потом (возможно через несколько лет), ломая ABI, объявить поддержку полной?
        Вроде бы так уже делают, и существенной разницы с Вашим подходом я не вижу.

        • antoshkka
          /#21301072

          С предложенным выше подходом можно просто писать «Реализовали C++23», что звучит намного лучше чем какая-то экспериментальная поддержка

          • Aldrog
            /#21301302

            В том-то и дело, что я не вижу разницы. Может быть в каких-то компаниях по каким-то причинам к формальному статусу поддержки стандарта компилятором относятся очень серьёзно, но я не понимаю, какие это могут быть причины. У нас фичи из нового стандарта обычно начинают внедряться до того, как вендор основного компилятора заявляет его полную поддержку.

            • khim
              /#21301450

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

              Да, я знаю, что в мире JavaScript — это нормально. В мире C++ — нет. Подавляющее большинство компаний «сидели на жопе ровно» и не трогали ничего из C++11 пока разработчики GCC не объявили «всё, поддержка C++0x более не является экспериментальной… можно пользовать» в GCC 5.

              Может быть в каких-то компаниях по каким-то причинам к формальному статусу поддержки стандарта компилятором относятся очень серьёзно, но я не понимаю, какие это могут быть причины.
              Причина проста: компилятор может потребоваться заменить (скажем с MSVC на ICC) или просто откатить (по тем или иным причинам — скажем новые версии GCC перестали поддерживать нужную вам фичу или платформу).

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

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

              Те же designate initializers возьмите: до clang 10 они банально небезопасны!

              Да, в основном это касается крупных проектов и огромных компаний… но поскольку они и являются главными спонсорами развития C++… то они заказывают музыку.

              • esaulenka
                /#21307442

                Те же designate initializers возьмите: до clang 10 они банально небезопасны!

                Приведите ссылочку, пожалуйста.
                Хочу знать, с какой стороны ждать грабли.
                Я использую для заполнения структур (например, вот этот файл можно почти безболезненно переименовать в cpp), и пока с проблемами не сталкивался. Зря использую?..

                • esaulenka
                  /#21307612

                  забыл ссылку добавить, извините:
                  дескрипторы USB создаются в compile-time и прямо в таком виде передаются "наружу"

                • khim
                  /#21307974

                  Хочу знать, с какой стороны ждать грабли.
                  Со стороны нарушений стандарта. Валидный C++20 код вроде бы все версии clang'а компилируют нормально — но если вы перепутаете порядок, то у вас может быть беда. И, главное, никакой диагностики (она в clang 10 появилась).

                  Приведите ссылочку, пожалуйста.
                  Одна, вторая, третья.

                  Их, в общем, много, но все они сводятся к одному: нарушения правил C++20 не диагностируются, а вместо диагностики может быть чёрт знает что.

                  Я использую для заполнения структур (например, вот этот файл можно почти безболезненно переименовать в cpp), и пока с проблемами не сталкивался. Зря использую?..
                  Это C. В C нет конструкторов и, соответственно, нет проблемы с порядком инициализации.

                  Мой косяк — не уточнил, то они небезопасны в C++. В C — вроде всё в порядке во всех версиях, где они поддерживаются. Думал из контекста понятно: в C они больше 20 лет назад появились, там они, конечно, экспериментальной фичей не являются.

                  • esaulenka
                    /#21308078

                    Спасибо, изучу.
                    И я неправильно выразился: по ссылке примеры использования библиотеки. Я что-то очень похожее пишу в cpp-файлах (общедоступной ссылки не дам), вроде б работает...

                    • khim
                      /#21308726

                      Собственно как я уже и написал: всё сводится к порядку. C++20 и GCC требуют, чтобы он совпадал с порядком описания полей типа, Clang — не требует. Причём -Werror=reorder-init-list Clang 10 только понимает, у более ранних этой диагностики нету.

                      Если бы он не требовал и ещё при этом всё и работало — ну так даже удобнее… к сожалению он не требует, а код, в результате, если это правило нарушить, может генерировать не работающий.

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

                      Обычная осторожность.

                      • Aldrog
                        /#21308908

                        На минутку, я не предлагал использовать ещё не стандартизованные фичи. Только удивлялся, как проект может отказываться от полезной фичи только из-за того, что поддержка стандарта, в который она входит, носит статус экспериментальной (как C++17 в gcc версий 6-8).


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

                        • khim
                          /#21310650

                          На минутку, я не предлагал использовать ещё не стандартизованные фичи.
                          А где вы тут увидели «нестандартизованные фичи»? Designated initializers — это часть стандартов C99 и C++20.

                          Только удивлялся, как проект может отказываться от полезной фичи только из-за того, что поддержка стандарта, в который она входит, носит статус экспериментальной (как C++17 в gcc версий 6-8).
                          Ну дык, блин, об этом и речь! Пока фича экспериментальная — ошибки в её реализации мало кого волнуют. Их могут годами править! А на вопрос «какого фига» ответ будет «фича экспериментальная, чего вы тут кипятитесь».

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

                          • Aldrog
                            /#21310920

                            А где вы тут увидели «нестандартизованные фичи»? Designated initializers — это часть стандартов C99 и C++20.

                            Он не был частью какого-либо стандарта C++ на момент выхода clang 9, о проблемах в котором вы пишете.


                            Проблема не в «переключить флаг». Проблема в том, что как только вы «переключаете флаг» — вы оказываетесь в дивном новом мире без поддержки и каких-либо гарантий.

                            Я не наблюдал в gcc 7 и 8 с флагом -std=c++17 бoльших проблем, чем при использовании «стабильно» поддерживаемых стандартов. А больше всего неожиданного поведения, несовместимого со стандартом я наблюдал на msvc независимо от используемых флагов.

                            • khim
                              /#21311064

                              Я не наблюдал в gcc 7 и 8 с флагом -std=c++17 бoльших проблем, чем при использовании «стабильно» поддерживаемых стандартов.
                              Рад за вас. Тем не менее это не отменяет того факта, что стандарт C++17 был принят уже после релиза gcc 7.

                              Он не был частью какого-либо стандарта C++ на момент выхода clang 9, о проблемах в котором вы пишете.
                              Его статус был ровно таким же, как фичи C++17 в GCC 7. Стандарт ещё не был утверждён в момент выхода GCC 7, извините.

                              То, что вы не напоролись ни на какие проблемы… ну — повезло вам. Я знаю кучу народу (ну как кучу… мои знакомые, коих, понятно, гораздо меньше чем всех программистов в любой компании подобной Google/IBM/Microsoft), которые эти designated initializers используют аж с 2012го года, когда они начали поддерживаться GCC 4.7 и Clang 3.0… и вроде у них всё работает. Может у них нет объектов со ссылками внутри или они просто всегда используют их в соотвествии со стандартами C++20… не знаю.

                              А у нас они оказались включены по недоразумению (они не поддерживаются в режиме -std=c++17 -pedantic, но поддерживаютс в -std=c++17 без -pedantic) — и это сразу привело к проблемам.

                              Эффект масштаба, однако.

                              А больше всего неожиданного поведения, несовместимого со стандартом я наблюдал на msvc независимо от используемых флагов.
                              Это — немного другая история. MSVC — это весьма часто боль, но к экспериментальным фичам это отношения не имеет.

                              • Aldrog
                                /#21319632

                                Рад за вас. Тем не менее это не отменяет того факта, что стандарт C++17 был принят уже после релиза gcc 7.

                                Да, перепутал версии. В проекте с gcc мы с восьмой версии -std=c++17 используем. В другом проекте на семнадцатый стандарт перешли одновременно с переходом на clang и какая это была версия я уже не смогу сходу восстановить (но это был 18-й год).


                                А у нас они оказались включены по недоразумению (они не поддерживаются в режиме -std=c++17 -pedantic, но поддерживаютс в -std=c++17 без -pedantic) — и это сразу привело к проблемам.

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

                                • Antervis
                                  /#21319990

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

      • khim
        /#21301628

        В стандарте и так уже полно опциональных фишек помеченных всякими __cpp_inheriting_constructors или __cpp_decltype_auto.

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

        И говорить «если XXX поддерживается компилятором, тогда YYY».

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

        • antoshkka
          /#21302860

          Они не опциональные, они все обязательные.

          Feature testing макросы сделаны для удобства разработчиков библиотек, на время переходного периода и для упрощения поддержки старых компиляторов.

          • khim
            /#21303034

            Они не опциональные, они все обязательные.
            Не все. intptr_t и int8_t — опциональны, FE_TONEAREST — тоже.

            Да, до сих пор в стандарте старались избегать опциональных компонент (они в результате оказываются в отдельных стандартах типа ISO/IEC DTR 18037), но я не уверен, что эта политика реально полезна.

  22. 4144
    /#21301974

    А метаклассы есть в каких-либо планах?

    • ZaMaZaN4iK
      /#21302060

      В планах есть — Саттер же толкает :) Когда будут — хз. Слабо верится, что успеют к С++23.

  23. Serzh4oK
    /#21303004 / +1

    клево, развиваемся потихоньку

  24. Centimo
    /#21311330 / +2

    минутка нытья
    Вот всякие рэнджи и корутины имплементировать время есть, а дыру несогласованности (void, который нельзя вернуть или создать переменную такого типа) в самом языке заделать не могут, хотя пропозал ещё с 2016 висит.

    • antoshkka
      /#21312396

      Можно же void возвращать: godbolt.org/z/k6Hpsi
      А void переменные вы для чего хотите использовать?

      • khim
        /#21312904

        Не знаю для чего можно использовать void-переменные, но знаю где можно применить void-ссылки.

        Например RPC. У нас есть небольшая самописная приблуда, использующаяся примерно так:

        auto&& [result, arg1, arg2] = RPCAdapter<int (*)(double, double)>(channel);
        
        Понятно что обычно вместо типа функции там какой-нибудь предопределённый тип.

        Так как ссылок на void не бывает, то приходится иметь кучу костылей в реализации — ну и там, где это используется тоже помнить, что для void-функций переменной result нету.

        А самое весёлое бывает когда функции — это просто функция. Без аргументов и кода возврата. Тогда получается
        auto&& [] = RPCAdapter<void (*)(void)>(channel);
        
        что далеко не всем компиляторам нравится… А clang после этого ещё и начинает заявлять, что, дескать, переменная [] не используется… а как её использовать предполагается, извините?

        • antoshkka
          /#21313240

          Мы с подобными предепреждениями боремся через атрибуты godbolt.org/z/RPVwRA:
          [[maybe_unused]] auto&& [] = RPCAdapter<void (*)(void)>(channel);


          А чем ссылка на void поможет в этом примере? Вы хотите чтобы structured bindings работали с void?

          • khim
            /#21315458 / +1

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

            Грубо говоря хочется, чтобы void был просто как пустая структура. Обычный тип, но без внутреннего содержимого.

        • Antervis
          /#21314054

          Так как ссылок на void не бывает, то приходится иметь кучу костылей в реализации
          так вы как ни изворачивайтесь всё равно рано или поздно особые свойства void вылезут. Например, вот здесь void пропадает из variadic pack'а. Поэтому (имо) зачастую проще его сразу обработать отдельным юзкейсом (благо constexpr if позволяет это сделать «не отходя от кассы»), чем пытаться писать исключительно универсальный и ни разу не читаемый шаблонный код.
          что далеко не всем компиляторам нравится…
          ну не знаю, я вот не компилятор, но мне тоже не нравится

          • mayorovp
            /#21314756

            Нет, в вашем примере void пропадает не из variadic pack'а, а из сигнатуры функции.


            (void) — это старый синтаксис, пришедший из тех времён, когда в Си простая запись () была эквивалентом (...). А может и сейчас является, не следил.

        • mayorovp
          /#21314764

          Хм, а что мешает написать вот так?


          RPCAdapter<void (*)(void)>(channel);

          • khim
            /#21315478

            То, что это получается ещё один случай в кодогенераторе.

            Не ручками же это всё пишется.

            И да, то, что f(void) — это уже «особый случай» печалит, конечно…

            • DistortNeo
              /#21315634

              Я реально не вижу каких-то сложностей в данном случае:


              auto&& [result, arg1, arg2] = RPCAdapter<int (*)(double, double)>(channel);
              auto&& [result] = RPCAdapter<int (*)()>(channel);

              У функции может быть переменное число аргументов, в том числе и нулевое. А единственный недостаток — std::tuple<> занимает не 0 байт.


              Проблема же в возвращаемом значении, но я бы эту проблему решил с помощью какого-нибудь result_wrapper<T> с отдельной перегрузкой для T = void.

              • khim
                /#21315804

                А единственный недостаток — std::tuple<> занимает не 0 байт.
                Это тут вообще причём? RPCAdapter — это не tuple.

                Проблема же в возвращаемом значении, но я бы эту проблему решил с помощью какого-нибудь result_wrapper<T> с отдельной перегрузкой для T = void.
                Там хоть перегружай, хоть не перегружай — всё равно синтаксис другой.

                Разрешили же делать return foo(); в функциях типа void (если foo тоже void возвращает). Вот и для других случаев того же хочется…

                • DistortNeo
                  /#21315986

                  Это тут вообще причём? RPCAdapter — это не tuple.

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

                  • khim
                    /#21316304

                    И про то, что пустое множество параметров в variadic template допустимо и не требует к себе какого-то особого отношения.
                    Я где-то утверждал обратное?

                    Про то, что этот тип вполне себе может сидеть внутри реализации.
                    Это уже проблемы реализации.

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

      • Centimo
        /#21319924 / +2

        Есть кусок кода, который выглядит примерно так:

        auto some_variable = some_function();
        ...
        auto some_variable_2 = some_function_2(some_variable);
        ...
        return some_variable;
        


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

        • Aldrog
          /#21320248

          Через if constexpr проблема достаточно легко обходится. А как компилятор потенциально должен обрабатывать вызов функции с аргументом типа void мне вообще не совсем понятно (синтаксис type function(void) — легаси из C).

          • AnthonyMikh
            /#21320430

            А как компилятор потенциально должен обрабатывать вызов функции с аргументом типа void мне вообще не совсем понятно.

            Например, так:


            void foo(int);
            template<typename T> int bar(T t);
            
            // Где-то далее в коде
            auto value = bar(foo(42));

            Фактически передачи данных не происходит, но bar(foo(42) создаёт зависимость по данным между вызовом bar и значением, возвращаемым foo(42), поэтому компилятор при оптимизации кода не может переупорядочить инструкции так, чтобы foo вызывалось после bar.

            • mayorovp
              /#21320520

              Зависимость по данным тут лишняя, она "развалится" если раскрыть любую из функций.


              Достаточно чтобы этот код просто компилировался.

          • mayorovp
            /#21320530

            Если там цепочка из достаточного количества вызовов — замаетесь вы if constexpr на каждый писать. Плюс возрастает риск допустить ошибку при копи-пасте очередного вызова внутрь if constexpr.