company_banner

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

    На днях в Праге прошла встреча международного комитета по стандартизации 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,6%Да440
    • 25,4%Нет150

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

    • 67,2%Да271
    • 32,8%Нет132
    Яндекс
    Как мы делаем Яндекс

    Комментарии 406

      +1

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

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

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

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

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

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

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

                +1
                Тоже случается, но очень редко. Как правило разработчики компиляторов оказываются «по одну сторону баррикад»: реализуемость фич коррелирует скорее со свойсвами фич, чем со свойствами компилятора.
              +2
              Да, участвуют. Все предложения проходят через них, без их олобрения предложение принято не будет
              0

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


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

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


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

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

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

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

                            Нет чего? Грамматики в Форме Бэкуса-Наура? Есть.

                      +1
                      А можно поподробней про первый пункт?
                        0

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


                        constexpr bool foo(int n) { ... }
                        
                        template<bool b> struct bar {
                            static int value;
                        }
                        
                        template <> struct bar<true> {
                            template<int n> value { }
                        }
                        
                        template <int n> struct baz : bar<foo(n)> { }
                        
                        baz<42>::value<3> v;

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

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

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

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

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

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

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

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

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

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

                            0
                            Добавляйте DNN (convolution / grouped-convolution) — это не всегда оптимально через GEMM из BLAS реализовывать.
                              0
                              Если вам чего-то не хватает в существующем предложении — пожалуйста, изложите подробно свои мысли и, желательно, напишите proposal. Так же приложите ссылки на предлагаемые для включения прототипы.
                            +4

                            Конечно есть!
                            https://wg21.link/P1385

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

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

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

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


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


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


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

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

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


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

                                            +4

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

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

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


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


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


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

                                                  0
                                                  Не весь код, а только импортированные символы. Потом для перекомпиляции зависимости можно не полагаться на timestamp source файла, а на хэш бинарного интерфейса модуля, что ещё уменьшит количество перекомпиляций.
                                                  Не, тут есть простор для оптимизаций.
                                            0
                                            Ranges и datetime — добавлили отличные библиотеки для рантайма
                                            Концепты — добавили для метапрограммирования и не только
                                            Модули — ускоряют компиляцию
                                            Корутины — позволяют использлвать нечто абсолютно новое
                                            Constexpr всякое — позволяют compile time вычисления делать с обычным синтаксисом
                                            Новые правила для тривиальных типов — ускорение рантайм кода


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                                          0

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


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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                                          +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.
                                                          Это вам хорошо рассказывать такие сказки если у вас в проекте только десяток человек и всё в одной организации. А если у вас сотни компонент от десятков вендоров, то переход растягивается на годы.
                                                            +2
                                                            Когда libstdc++.so.6 появился (а было это в далёком от нас уже сегодня 2004м году) — расколбас был конкретный. А тогда библиотек было ещё написано куда как меньше, чем сегодня.

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


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

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


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

                                                              +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му году — комитет не захотел.
                                                            0
                                                            Там проблема глубже. На libstdc++.so.6 + libstdc++.so.7 проблемы только начинаются: вам нужно собрать отдельный Boost для libstdc++.so.6 и отдельный для libstdc++.so.7… фактически каждая популярная C++ библиотека удвоится.

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

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

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

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

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

                                                                0
                                                                мост между ABI

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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


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

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

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

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

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


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

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

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

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

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

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


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

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

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

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


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

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


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

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

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

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

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

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

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

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


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

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


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

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

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

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


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


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

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

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

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


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

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

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

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

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

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

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

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

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


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

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

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

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

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


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


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

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

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

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

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

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

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

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

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

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

                                                                                                        0

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

                                                                                                          0

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                                                                                                Кстати, добавьте noexcept к vector::back прям сейчас… и вы не увидите разницы в кодгене для GCC (остальные вендоры ленятся с правильной реализацией исключений)
                                                                                                                  0
                                                                                                                  Если трейт начнёт врать, то вектор возможно что всегда будет копировать. А это безумные замедления, никакие мнимые преимущества от noexcept их не перевесят.
                                                                                                                  трейт начнет врать если мы начнем навешивать контракты на мув конструкторы/операторы присваивания. А это скорее странный/нежелательный юзкейс нежели типовой
                                                                                                                  Кстати, добавьте noexcept к vector::back прям сейчас…
                                                                                                                  vector::back сравнительно хорошо инлайнится, а для более сложных функций разница будет заметной.
                                                                                                                  остальные вендоры ленятся с правильной реализацией исключений
                                                                                                                  да с исключениями вообще всё очень плохо. И поэтому noexcept так важен
                                                                                                                    +1
                                                                                                                    кстати, можно же еще вопрос иначе поставить: может быть разрешить стандартным библиотекам усиливать noexcept'ness гарантии для не кидающих функций? Т.е. пометить набор функций в стандарте как noexcept(/*implementation-defined*/)?
                                                                                                                      +1

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


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

                                                                                                                        0

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

                                                                                                                          0
                                                                                                                          И тут разработчики MS VC «А я говорил, говорил!» :D
                                                                                                                0

                                                                                                                А 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
                                                                                                                  +1
                                                                                                                  Используйте это на свой страх и риск. Как будут выглядеть стандартные модули ещё не решено, только зарезервированы имена начинающиеся с std. Если к C++23 комитет решит сделать несколько другие имена модулей, код с вышеозвученными MS модулями немного поломается.
                                                                                                                    0
                                                                                                                    А такие конструкции
                                                                                                                    import <iostream>;
                                                                                                                    как относятся к стандарту?
                                                                                                                      +1
                                                                                                                      Импортирование C++ хедеров обязано работать в C++20. import <iostream>; — это правильно, безопасно.

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