company_banner

С++20 на подходе! Встреча в Рапперсвил-Йона

    В начале июня в городе Рапперсвил-Йона завершилась встреча международной рабочей группы WG21 по стандартизации C++.

    Вот что вас ждёт под катом:
    • Контракты и друзья
    • Концепты (без друзей)
    • __has_cpp_attribute(unlikely)
    • bit_cast<my_stuff>(some_array)
    • contains, shift_left, shift_right, ispow2, ceil2… и старые алгоритмы под новым соусом
    • atomic_ref
    • Что нового можно писать в шаблонах и чем это полезно
    • constexpr virtual foo()
    • Parallelism 2, Reflection и Executors TS

    Также будет бонус: минисекция для экспертов:

    • user-declared virtual destructor не влияет на тривиальность типа
    • Куда можно будет засунуть восклицательный знак и чем это может быть полезно
    • constexpr std::regex mail_regex(R"((?:(?:[^<>()\[\].,;:\s@\"]+(?:\.[^<>()\[\].,;:\s@\"]+)*)|\".+\")@(?:(?:[^<>()\[\].,;:\s@\"]+\.)+[^<>()\[\].,;:\s@\"]{2,}))")


    Контракты


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

    std::string get_name_by_login(std::string_view login)
        [[expects: !login.empty() ]]
        [[ensures ret_value: !ret_value.empty() ]]
    ;
    

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

    • Компилятор (в зависимости от флагов компиляции) сгенерирует ассерты.
    • Компилятор (в зависимости от флагов компиляции) сможет делать выводы и оптимизировать код на основе их. Так, например, сможет полностью удалить проверку на пустоту возвращаемого значения в пользовательском коде.
    • Сторонняя утилита (например, doxygen) сможет сгенерировать более полную документацию в функции без необходимости явно прописывать пред/пост условия.
    • Статические анализаторы смогут проанализировать код и найти ситуации, когда предусловие гарантированно нарушается (да, без всяких рантайм тестов).

    Подробности по контрактам доступны в официальной бумаге.

    На помощь контрактам спешит библиотека от РГ21 и Fails, позволяющая сохранять и печатать стектрейс. Так можно будет задать обработчик проваленного контракта и печатать в нём последовательность вызовов, которая привела к проблеме:

    void(const std::contract_violation & e) noexcept {
        std::cerr << "Contract violated in function " << e.function_name() << '\n'
            << std::stacktrace();
    }
    


    При нарушении контракта мы увидим подобное сообщение:

    Contract violated in function std::array<T, N>::operator[](size_type) [with T = int; long unsigned int N = 5ul; ]': 
     0# std::array<int, 5ul>::operator[](unsigned long) at /usr/include/c++/array:124
     1# bar(int) at ../example/assert_handler.cpp:17
     2# foo(int) at ../example/assert_handler.cpp:25
     3# main at ../example/assert_handler.cpp:54
     4# 0x00007F991FD69F45 in /lib/x86_64-linux-gnu/libc.so.6
     5# 0x0000000000401139
    }
    

    std::stacktrace ещё не принят в C++20, но прошёл design review в группе LEWG, и осталась только работа по приведению его описания в соответствие с правилами стандарта в группе LWG. Последняя спецификация доступна по ссылке.

    Концепты


    Концепты как фича языка были уже приняты на прошлом заседании комитета, а в этот раз в стандартную библиотеку приняли основные концепты из Ranges TS. Так что теперь можно пользоваться готовыми концептами для описания compile-time требований к шаблонным параметрам и не мучаться с написанием собственных:

    template <class F>
        requires Invocable<F>
    void my_executor::execute(F f) noexcept {
        lock_guard l{data_mutex_};
        push(std::move(f));
    }
    

    Полный список концептов и их описания доступны в этом proposal.

    Feature-test macros


    Уже длительное время компиляторы и стандартные библиотеки предоставляют макросы для описания возможностей компилятора. Теперь эти макросы официально являются частью стандарта и даже были расширены. Так можно будет проверить поддержку компилятором аттрибута unlikely, используя выражение __has_cpp_attribute(unlikely).

    Список макросов, принятых на заседании доступен в бумаге.

    bit_cast


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

    Использовать её легко, просто замените

     my_type my = reinterpret_cast<my_type&>(some_array); 
    на
    my_type my = std::bit_cast<my_type>(some_array); 

    Теперь, если размеры some_array и my_type не совпадают, или если вы пытаетесь преобразовать нетривиально копируемые типы, вы получите сообщение об ошибке на этапе компиляции. В остальных случаях вы избежите всех страшных ошибок, связанных с type aliasing.

    Алгоритмы и новые функции


    Вот некоторые полезные вещи, добавленные в стандартную библиотеку C++20:

    • shift_left(it_begin, it_end, unsigned n) – сдвигает значения в диапазоне влево на n, как если бы вызывали *it_begin = std::move(*(it_begin + n)), *(it_begin + 1) = std::move(*(it_begin + n + 1))...
    • shift_right(it_begin, it_end, unsigned n) – аналогично алгоритму выше, но двигает диапазон право, как если бы вызывали *(it_begin + n) = std::move(*it_begin), *(it_begin + n + 1) = std::move(*(it_begin + 1))...
    • ispow2(x) – вернёт true если в него передали число, являющееся степенью двойки
    • ceil2(x) – округляет число в большую сторону до ближайшего числа, являющегося степенью двойки
    • contains – все ассоциативные контейнеры обзавелись функцией bool contains(const key& v), которая возвращает true если ключ содержится в контейнере

    В добавок, уже имеющиеся алгоритмы, использующие std::swap и сам std::swap стали constexpr. Так что теперь можно сортировать, вызывать std::nth_element и получать их результаты на этапе компиляции. Все подробности доступны в бумаге от РГ21.

    atomic_ref


    Вы работаете со структурами данных в основном из одного потока, но в какой-то момент вам необходимо работать с полем атомарно? Для этих случаев был добавлен шаблонный класс atomic_ref<T> (формальное описание доступно в бумаге).

    При этом надо помнить, что атомарность гарантируется только для операций, производимых через экземпляры классов atomic_ref<T>. Так что если вы в одном потоке пишете в переменную без atomic_ref, а в другом потоке читаете через atomic_ref, то в итоге ничего хорошего не получится.

    Кстати, если вам не нравятся неконсистентные имена типов (string_view, atomic_ref), то выскажите своё недовольство по ссылке. Если будет достаточное количество голосов, постараемся все имена привести к одному виду.

    Экземпляры классов как шаблонные параметры


    Если вы написали класс X, и для него написали operator<=> следующего вида:

    struct X {
        // ...
        std::strong_equality operator<=>(const X&, const X&) = default;
        // ...
    };
    

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

    template <X x>
    struct x_as_template_param {
        // ...
    };
    

    Однако комитету ещё предстоит большая работа по модернизации стандартной библиотеки и переводу различных её частей на использование operator<=>.

    constexpr virtual


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

    При этом если в базовом классе у вас имеется метод constexpr virtual int foo();, то в наследнике int foo() может быть как constexpr, так и нет. При вызове foo() на этапе компиляции компилятор не даст вам скомпилировать программу, если вы будете пытаться выполнить не constexpr виртуальную функцию.

    Данное изменение, например, открывает двери для реализации применимого на этапе компиляции std::type_info, что позволит этому классу получить функционал Boost.TypeIndex, с возможностью гарантированной compile-time работы с типами:

    template <class T, class U>
    constexpr bool is_same() {
        constexpr bool res = (typeid(T) == typeid(U));
        return res;
    }
    

    Последние подробности доступны в бумаге.

    Parallelism 2, Reflection и Executors TS


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

    Reflection готовится к выпуску в виде технической спецификации (TS). В нём рефлексия делается всё ещё через механизмы, схожие с <type_traits>. В планах донести до ядра языка больше функционала для constexpr вычислений и переделать рефлексию на constexpr! функции (см. ниже).

    Executors в C++20, скорее всего, не попадут, а будут выпущены в виде отдельного TS. Возможно, что весь их дизайн будет пересмотрен и упрощён.

    user-declared virtual destructor и тривиальность типа


    Изменение, которое должно попасть в C++20 (на следующем заседании), повлияет на производительность различных частей стандартной библиотеки и оптимизации компиляторов, но вряд ли будет заметно в повседневной разработке:

    struct i_am_trivial {
        int foo;
        char bar;
    
        virtual ~i_am_trivial() = default;
    };
    

    Идея в том, что если деструктор виртуальный, это ещё не значит что он нетривиальный. Так стандартная библиотека сможет понимать, что тип, с которым она работает, хоть и имеет виртуальный деструктор, на самом деле не требует вызова деструктора для освобождения ресурсов. Это может дать прирост производительности, например, для std::vector<Base>, где Base имеет такой виртуальный деструктор.

    constexpr!


    Ещё одно занятное изменение, которое рассматривается для приёма в C++20 – это constexpr! функции.

    Такие функции обязаны выполняться только на этапе компиляции, любая попытка использовать их в runtime приведёт к ошибке компиляции. Это одно из изменений необходимых для рефлексии в C++.

    Дополнительным бонусом constexpr! функций является их эффективное использование ресурсов компилятора. Т.к. constexpr! функции не выполняются на рантайме, компилятор не должен генерировать промежуточное представление (машинные инструкции) для них и не должен держать их памяти или оптимизировать. Это резко уменьшает количество оперативной памяти, требуемой компилятору и несколько ускоряет компиляцию. Должно оказывать ощутимый эффект на современные библиотеки, сильно полагающиеся на метапрограммирование, как Boost.Hana или [Boost.]PFR.

    Вместо итогов: constexpr std::regex


    Множество языков программирования в данный момент компилируют/транслируют регулярные выражения ещё перед запуском программы. Таким образом, когда программа стартует, все регулярные выражения уже преобразованы в соптимизированный конечный автомат.

    В C++ это не так:

    bool is_valid_mail(std::string_view mail) {
        static const std::regex mail_regex(R"((?:(?:[^<>()\[\].,;:\s@\"]+(?:\.[^<>()\[\].,;:\s@\"]+)*)|\".+\")@(?:(?:[^<>()\[\].,;:\s@\"]+\.)+[^<>()\[\].,;:\s@\"]{2,}))");
    
        return std::regex_match(
            std::cbegin(mail),
            std::cend(mail),
            mail_regex
        );
    }
    

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

    С готовящимися новинками для constexpr вычислений (constexpr new, is_constexpr_evaluated() и др.) можно будет в C++ делать множество вещей на этапе компиляции, в том числе можно будет сделать constexpr std::regex.

    С constexpr std::regex конечный автомат для функции is_valid_mail() построится ещё на этапе компиляции. Более того, GCC сможет генерировать оптимизированные регулярки на этапе компиляции без static const, т.к. начиная с GCC-6 если у constexpr функции все параметры на вход – константы, GCC форсирует вычисление на этапе компиляции.

    Так вот, как вам идея сделать constexpr std::regex?

    P.S.: Для желающих денег C++ практики с недавнего времени есть Yandex.Taxi Coding Fest. Можно будет вооружиться и побеждать соперников используя C++17.
    Яндекс 446,14
    Как мы делаем Яндекс
    Поделиться публикацией
    Комментарии 427
    • +1
      а концепты попадут в стандарт только в виде required clause (1) или варианты 2/3 тоже «доедут»?
      // 1
      template <typename T>
          requires Copyable<T>()
      void foo(T x);
      // 2
      template <Copyable T>
      void foo(T x);
      // 3
      void foo(Copyable x);
      


      А есть ли возможность сделать перегрузки constexpr/runtime для функций? Отсутствие этой фичи не позволяет считать на этапе компиляции многие математические функции, в т.ч. exp/log.

      Какой статус у Ranges?
      • +2
        Required clause (1) вошли и будут в C++20.
        Варианты 2 и 3 активно обсуждаются и полируются, в них есть проблемы:
        • Вариант 2 не позволяет работать с концептами принимающими сразу два шаблонных параметра
        • Вариант 3 вызывает недоумение в случае
          void foo(Copyable x, Copyable н);
          Типы у переменных x и y должны быть одинаковые или нет?


        А есть ли возможность сделать перегрузки constexpr/runtime для функций?

        Идёт работа над волшебной функцией is_constant_evalueated(). Она позволит внутри функции делать ветвление, в зависимости от того, выполняется функция на рантайме или на этапе компиляции:
            constexpr double power(double b, int x) {
              if (std::is_constant_evaluated() && x >= 0) {
                // A constant-evaluation context: Use a
        	// constexpr-friendly algorithm.
                double r = 1.0, p = b;
                unsigned u = (unsigned)x;
                while (u != 0) {
                  if (u & 1) r *= p;
                  u /= 2;
                  p *= p;
                }
                return r;
              } else {
                // Let the code generator figure it out.
                return std::pow(b, (double)x);
              }
            }


        Какой статус у Ranges?

        Идёт активная работа по принятию из в C++20. Есть все шансы на успех.
        • +3
          Вариант 2 не позволяет работать с концептами принимающими сразу два шаблонных параметра

          это критично с точки зрения РГ даже с учетом того, что для этого есть fallback синтаксис?
          Типы у переменных x и y должны быть одинаковые или нет?

          (мне) очевидно, что нет. Неужели это является решающим фактором?
          • +1
            Полностью разделяю ваше мнение! Но в комитете много людей, и у них свои задачи и требования к фичам языка.

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

            Кому-то не нравится вариант 2, потому что он громоздкий :)
      • +1

        Про модули и сопрограммы что-то известно? Herb Sutter даёт прогноз, что сопрограммы скорее всего войдут, а насчёт модулей нет ясности.

        • 0
          По модулям будет отдельная встреча этим летом. Будут обсуждать только модули.

          Буквально 2 заседания назад появилось новое предложение по модулям, позволяющее экспортировать макросы и упрощающее миграцию старого кода на модули. На ближайших заседаниях будут пытаться слепить старые и новые предложения в одно. Общее предложение скорее всего будет сначала выкачено в эксперимент (в виде обновленного Modules TS), и только после этого смержено в стандарт. В общем, шансы увидеть модули в C++20 есть, но они не велики.

          Сопрограммы скорее всего войдут в C++20. Были попытки выдвинуть другое (конкурирующее) предложение по сопрограммам. Поддержки новый подход не получил. Если новых предложений или возражений не поступит, то Coroutines TS окажется в C++20.
          • +2
            Что мешает просто договориться, что модули и макросы несовместимы? Ведь все равно с приходом модулей придется библиотеки переписывать в какой-то степени, почему бы «под шумок» не выкинуть оттуда макросы, заменив на всякую constexpr-магию? И волки сыты и овцы целы.
            • +2
              Потому что многих людей не устраивает, что придётся код перелопачивать сильно на новые модули. Поэтому и придумывают, как бы так безболезненно заменить хедеры на модули с оставшимися экспортированными макросами, так как есть много кода, который ну очень сильно завязан на макросы.
              • +2
                многих людей не устраивает, что придётся код перелопачивать сильно на новые модули

                Помню как Дельфи помер, когда очередная новая версия оказалась потерявшей совместимость со старым кодом.
              • +1
                Это текущий Modules TS (модули в первоначальном виде).

                Ряду компаний очень не понравилось отсутствие макросов и, как следствие, невозможность «модуляризировать» системные заголовочные файлы.
                • 0
                  а то, что макросы плюют на namespace и чужой код, а также в разы более убогие, чем constexpr, ряду компаний нравится?
                  • 0
                    Хочу напомнить, что некоторые вещи без макросов в С++ по-прежнему нельзя реализовать.
                    Или я просто не знаю, как, но что-то сомневаюсь.
                    • 0
                      Какие, например?

                      Я не отрицаю их существование, просто лично мне это нужно очень редко, а с компилтайм-рефлексией мне это не нужно будет вообще.
                      • 0
                        И для рефлексии в том числе, но не только. Например, макрос, который я назвал EXEC_ON_SCOPE_EXIT, принимающий лямбду и создающий на стеке объект, выполняющий эту лямбду в деструкторе. Тонкость в автоматическом именовании этого объекта, чтобы включал номер строки (что гарантирует уникальность, если не пихать по два в строку):

                        #define EXEC_ON_SCOPE_EXIT auto CONCAT_EXPANDED_ARGUMENTS_2(scope_exit_executor_, __LINE__) = detail::makeOnScopeExitExecutor


                        В общем случае, макросы, использующие стрингификацию и/или конкатенацию строк, обычно решают задачи, не решаемые средствами С++.
                        • 0
                          Тонкость в автоматическом именовании этого объекта

                          Согласен с этим случаем. Просто мне как-то, наверное, никогда не нужно было в рамках одной функции создавать хотя бы два таких гарда, которые не нужно было бы запихнуть в специальный реюзабельный RAII-класс. А так const auto guard = MakeScopeGuard(...) не лень написать, зато код на меня не кричит.

                          В общем случае, макросы, использующие стрингификацию и/или конкатенацию строк, обычно решают задачи, не решаемые средствами С++.

                          Правильный reflexpr умеет стрингифицировать без всяких макросов.
                          • 0
                            зато код на меня не кричит

                            Вы имеете в виду upper case? Можно же переименовать :)
                            То, что вы предлагаете, функционально тождественно, но много лишних символов, я от такого люблю избавлять программиста.
                            reflexpr

                            Это что-то, чего мы ждём в С++20? Очень мало результатов в Гугле по этому термину.
                            • 0
                              Это что-то, чего мы ждём в С++20? Очень мало результатов в Гугле по этому термину.

                              Это такое вольное название для компилтайм-рефлексии, произошедшее от одной из, как бы это сказать, этаких компилтайм-функций для её поддержки.
                          • 0
                            всё жду, когда это полноценно имплементируют в основных компиляторах:
                            en.cppreference.com/w/cpp/experimental/source_location
                            Пока есть только в gcc
                          • 0
                            Например, constexpr преобразование указателей. Что-то типа:
                            static constexpr volatile uint32_t* MY_PTR = (volatile uint32_t*)0x12345678;
                            А без этого невозможен никакой embedded и низкоуровневое системное программирование. Приходится всё только на многоуровневых извращённых макросах делать.
                            Колоссальный косяк, кстати, который не позволяет писать под железо на полноценных плюсах. А всё потому, что бородатые дядьки опять заигрались в стандарты.
                    • +1
                      Тогда просто не будут переходить на модули те, у кого макросы в апи. Тот же gtest/gmock, например.
                      • +2
                        В текущем виде да. Или они там могут просто дождаться модулей+рефлекшн и переписать свои фреймворки на тот же подход, который используют C#, Java и прочие. Понятно что переписывать никто ничего не хочет, но это цена прогресса.
                        • +1
                          Вот и получается, что либо ждем лет 10-15 всех необходимых фич, либо поддерживаем макросы в модулях и делаем быстрый и безболезненный переход.
                          • 0
                            И практически не получаем выигрыша в производительности :)
                            • 0
                              А вот тут хочется подробностей, полагаю, что авторы Modules TS и ATOM должны были проводить какие-то эксперименты на своих кодовых базах.

                              Я читал про эксперимент на модулях от clang (а авторы proposal наверняка вдохновлялись и такими модулями), они давали прирост 60% (или 10% по сравнению с использованием precompiled headers).

                              Хочется еще узнать, а как реализованы Modules TS и ATOM в gcc. Используют ли они предложенное Страуструпом бинарное представление пропарсенного C++ кода? Или у них какое-то свое представление? И таскают ли в реализации ATOM текстовую копию кода для поддержки экспорта макросов?
                              • +2
                                Работать 10 лет и получить прирост скорости компиляции на 10% больший чем дают precompiled headers — это провал. Люди работающие над clang — одни из авторов ATOM. Можно приблизительно представить, какой прирост производительности с подходом из ATOM можно получить.

                                Один из разработчиков Modules TS утверждает, что макросы — одна из основных причин тормозов. Поэтому пока вы не начнёте использовать модели без макросов — довольствуйтесь 10% прироста скорости.

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

                                Modules TS и ATOM в gcc сделаны и информация об актуальном состоянии есть вот тут gcc.gnu.org/wiki/cxx-modules
                                • 0
                                  Вот поэтому и хочется узнать о результатах экспериментов от разрабов Modules TS. Что-то вроде: «Вот мы сделали модули без макросов и компиляция ускорилась в 10 раз, а сделали с макросами — получили прирост в 0,1».
                                  Может подход в духе precompiled headers и достигает теоретического предела в приросте?

                                  Я ни в коем случае не защищаю макросы и не пишу своих в кодовой базе. Я заинтересован в том, чтобы разработчики библиотек активно переходили на модули, но как бы не получилось так, что будем еще 10 лет разрабатывать модули, а потом 10 лет переходить на них.
                                  • 0
                                    Такое ощущение будто вы хотите одним седалищем усесться на два стула. Чтобы и модули были, и легаси не пришлось переписывать. Так, скорее всего, уже не получится. Если в модулях будут макросы, польза от их введения будет весьма условной.
                                    • 0
                                      Лично я не жду от модулей в языке с открытой внешней компоновкой никакой пользы кроме вреда. Яву уже превратили в породистую корову, несущую несколько дорогих разукрашенных стампидных седел. Пришла очередь C++?
                                      • 0
                                        Что это за зверь такой «открытая внешняя компоновка»? C++ единственный язык который до сих пор не имеет концепции модулей, которая даже старше ООП и появилась с изобретением процедурного и структурного программирования. Чтобы использовать библиотеки в C++ нужно как в С лезть на низкий уровень и заниматься флагами компилятора, служебными макросами (#ifdef/#endif), включением исходных файлов (в C это оправдано, поскольку язык предназначен для системного программирования и прекрасно для этого подходит). Еще «лучше», т.н. header-only библиотеки, которые часто используют еще и шаблоны что приводит к непомерному дублированию кода, ведь полная оптимизация возможно только в полностью функциональном языке, но никак не в императивном.
                                        • 0
                                          Простите за это очевидное объяснение, но хочется избежать недопонимания.
                                          Возможно, моя русская терминология неточна: она формировалась в 90 годы прошлого века во время чтения сделанных издательством «Мир» переводов классических англоязычных руководств по C и С++. Имеется в виду тот факт, что язык C++ работает с отдельными единицами компиляции, превращая их в объектные файлы определенного для целевой платформы формата (с функциями и глобальными переменными). Этот формат, соглашения о вызовах и ABI, устройство выполнимых бинарных образов и разделяемых библиотек, строго говоря, к языку C++ не относится, а относится к целевой платформе и в основных чертах остается неизменными со времени изобретения ассемблера. В частности, часть платформы — компоновщик, перебирает указанные ему объектные файлы, смотрит, на какие имена внешних объектов они ссылаются и найденные объекты (тела функций и определения глобальных переменных) как-то платформозависимо укладывает в выполнимый файл. Если компоновщик не найдет нужный объект — будет ошибка времени компоновки.
                                          Противоположностью этому являются варианты явной компоновки, когда компилятор формирует бинарные файлы не только со скомпилированным кодом, но и с мета-информацией о видимых снаружи именах. Такие файлы и называются (обычно) модулями. В единице компиляции требуется указывать не только имя внешней сущности (функции или переменной), но и то, из какого модуля ее следует брать. В этом случае компоновщик не ищет первое попавшееся имя, а ищет конкретный объект в конкретном модуле, что и быстрее, и «типобезопаснее». Примеры — Turbo Pascal и Modula2, C#, можно продолжить.
                                          Совместимость с C и используемый в C (взятый из экосистемы ассемблера) инструментарий — совершенно сознательное решение Страуструпа, сделанное при создании C++. По моему мнению, это решение сыграло важнейшую роль в том взрывном распространении C++, которое мы наблюдали в 90 годы XX столетия))).
                                          И мне не очень понятно, где тут место для сколь-нибудь развитой системы модулей. Опасаюсь, что в результате такого «скрещивания ужа с ежом» получится нечто, что будет смотреться столь же органично, как седло на корове или пятая нога у собаки. Впрочем, это мое личное мнение и я его никому не навязываю.
                                          • 0
                                            Информация о символах есть всегда. В тех же объектных файлах, или как вы себе представляете работу компоновщика? То что сам язык об этом не знает, это большой минус языка. С другой стороны, если язык ограничен в ресурсах, а C был системным языком для ограниченных в ресурсах платформ, то поэтому делегирование такого знания компоновщику было элегантным решением. Но для C++ это — тяжелое наследие детства с «игрушками прибитыми к полу» минимальной рудиментарной системой модулей (те же объектные файлы и т.н. «библиотеки» — архивы объектных файлов).
                                • 0
                                  С чем именно это сравнивалось? Наиболее тормозной вещью в сборке С++-кода является раздельная компиляция. Дефолтная ситуация выглядит так: есть один cpp-файл на 1к строк, в него инклюдится 100500 хедоров на 1кк строк, тем самым каждый файл раздувается до 1к1к строк.

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

                                  • +1
                                    Верно. Распишу поподробнее:

                                    Когда вы собираете проект, каждый cpp файл может компилироваться параллельно (это хорошо, отличная масштабируемость).

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

                                    Ещё одна проблема — невероятно количество макросов (они пораждаются например include guards). Держать их всех в памяти и препроцессировать документ становится тяжёлой задачей.

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

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

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

                                    И наконец, финальная стадия сборки проекта — линковка. В данный момент линковщик много времени тратит на выкидывание одинаковых блоков скомпилированного кода (вы подключили <string> 100 раз — линкер выкинет 99 скомпилированных std::string). С модулями линкеру не придется этим заниматься, т.к. файл модуля не будет вкомпиливаться внутрь собранного cpp файла.

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

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

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

                                      Оно одновременно и замедляет и ускоряет. Замедляет за счёт того, что происходит более глобальный ipo, а ускоряет за счёт того, что не нужно 10раз оптимизировать одни и те же функции в контексте того же header-only, которого в С++ предостаточно.

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

                                      Макросы так же засирают глобальный неймспейс.

                                      Если модули позволят перевести весь легаси-код в модули из которых не будут торчать сишные кишки, макросы и тому подобное — это будет прекрасно. Для чего же они современному коду, особенно если использовать модные/набирающие популярность техники, я не понимаю.
                                      • 0
                                        Все еще не понятно, действительно ли модули смогут значительно ускорить сборку по сравнению с unity билдами. Не очень похоже, что препроцессниг макросов занимает значительное время.

                                        И сразу открытый вопрос: а почему нельзя совместить оба подхода, с экспортом макросов и без? Даже если модуль с макросами будет собираться дольше, пусть это будет опцией при сборке модуля. Если опция не включена, то и хранить модуль можно в более оптимальном виде, и знать можно, что он не повляет на последующие текстовые инклюды. Дескать, если разработчик настолько сильно желает экспортировать макросы в своем апи, что готов пожертвовать временем компиляции у конечных потребителей — пусть. Пользователям решать, использовать такую библиотеку или нет.
                                        • 0
                                          Юнити билды становится адово использовать в случае локальной разработки. Скажи разработчику что каждый инкрементный билд будет по полминуты занимать (вместо 1-2 секунд) — он криво на тебя посмотрит.
                                          Да и на сервере, не так чтобы сильно ускоряется сборка. Я делал такую штуку — в cmake делал автоматическое объединение cpp всей цели для одной либы. Ускорение — не больше, чем в два раза. И плюс мы получаем тьму неудобств от необходимости следить за анонимными неймспейсами и т.п.
                                          Если еще и локально одна схема, а на билде другая — это вообще адъ.

                                          Пока лично для меня самая эффективная схема — распределенная сборка) Железо докупить оказывается дешевле. Когда сборка параллелится на 100+ ядер — ускорение налицо. Понятно, что и там есть ограничение сверху (возможность параллелить, возможности сети), но зато эффектом можно пользоваться и на CI, и на дев машине.

                                          Я пока раздумываю как в свою систему распределённой сборки модули прикручивать. Получается, что по сети не только единицу трансляции гонять придется, но и все используемые скомпиленные модули.
                                          • 0
                                            У меня коллеги ускорили сборку своего проекта с 15 до 5 минут с помощью unity билдов. И думаю, что скорость инкреметного билда их не сильно разражает, т.к. большую часть этого времени все равно занимает линковка пачки статических зависимостей.

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

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

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

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

                                              Ну и компоновка всего проекта как кучи статичных либ — вообще сомнительное удовольствие)

                                              С юнити билдами кроме скорости сборки, повторюсь, лично для нас основная проблема — конфликт имен (и иногда хедеров). Считай, прощай уютный анонимный неймспейс.
                                              По хедерам — допустим есть либа, которая оборачивает два сторонних API, предоставляя общий интерфейс. По отдельности хедеры собираются норм, а когда компилишь вместе — хедеры реализаций начинают конфликтовать.

                                              Ах да, еще замечательное — т.к. все файлы идут вместе, начинает теряться необходимый набор хедеров в каждой единице трансляции. Начинаются вещи вроде «убрали один cpp файл — все отвалилось», а файл этот может исключаться из сборки какой-то опцией, т.е. нужно тестировать все комбинации опций при вообще любой правке, даже не в хедерах.
                                              В общем, в плане поддержки это очень тяжело. От распределённой сборки все же треба только железо/сеть, ты никак не думаешь о каком-то специфичном коде.
                                      • 0
                                        Детали эксперимента можно тут посмотерть: reddit. Это всего лишь локальный эксперимент какого-то разработчика, но все же лучше, чем ничего.

                                        Если рассуждать о скорости компиляции, то модули подступаются к этому вопросу с нескольких сторон.

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

                                        Или, например, объявление модуля. Не знаю, как там после последней встречи объединили Modules TS и ATOM, но конкретно от ATOM ранее было предложение разрешать объявление модуля только на верху единицы трансляции. Это позволит компилятору прочесть только заголовки файлов и построить граф зависимостей между файлами, чтобы потом оптимальнее подгружать их при компиляции.

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

                                          Нет, они этого практически физически не могут сделать, так как на каждый TU запускается свой инстанс компилятора, которые (в имеющихся реализациях) не имеют общего состояния.
                                          • +2

                                            Zapcc делает именно это — кэширует инстанциации между ТУ. Для этого они держат в памяти сервер с кэшем.

                                            • 0
                                              Да, забыл про него. В его случае это возможно.
                                              • 0
                                                Из того как работает zapcc, я так понимаю, модули его не сильно и ускорят, он и так каждый файл препроцессированным держит? Можно интересно считать, что то ускорение, которое дает zapcc- оценка сверху для ускорения от модулей?
                                          • 0
                                            Согласитесь, если вам вместо файла с исходниками дадут вот такую единицу модуля — компилироваться все в итоговый файл будет быстрее.

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

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

                                            Опять же, один из тех же профитов, который даёт header-only — простую кросскомпиляцию и между платформами и между компиляторами. Дадут ли тоже самое модули? Сомневаюсь.

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

                                            Поэтому совместно с шаблонами используется header-only подход.

                                            • 0
                                              Мы потеряем то самое преимущество, особенно в контексте header-only, когда мы всегда можем увидеть как минимум сигнатуры функций.

                                              Не совсем понял. Под «увидеть» понимается посмотреть на код в human-readable формате? Если да, то вопрос тулчейна, он должен научиться показывать интерфейс модуля в читаемом виде, все необходимое для этого в модуле есть.

                                              Опять же, один из тех же профитов, который даёт header-only — простую кросскомпиляцию и между платформами и между компиляторами. Дадут ли тоже самое модули? Сомневаюсь.

                                              Какая связь между кроссплатформенностью и header-only? Как в header-only спокойно можно писать непереносимый код, так и не в header-only можно писать кроссплатформенно. Это ортогональные понятия.
                                              • 0
                                                Если да, то вопрос тулчейна

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

                                                Какая связь между кроссплатформенностью и header-only?

                                                Прямая.

                                                Как в header-only спокойно можно писать непереносимый код, так и не в header-only можно писать кроссплатформенно.

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

                                                Опять же, самая сложная проблема — собрать всё, собрать все зависимости. Дефолтный подход требует системы сборки, где добиться кроссплатформенной сборки — то ещё приключение. Даже не смотря на модный нынче cmake, и попытки создавать пакетные менеджеры.

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

                                                Далее, дистрибуция. Это опять приключения. Нужна статическая линковка, либо приключения с контейнерами. Статическая линкова сама по себе то ещё приключение и мало кто её поддерживает из зависимостей.

                                                В случае же с header-only нет никаких проблем с дистрибуцией самих зависимостей — просто носишь собой/забираешь из vcs. Нет никаких проблем со сборкой — нет никакой сборки вообще. Нет никаких проблем с дальнейшей дистрибуцией бинарей — нет никаких бинарные зависимостей, линковки и прочих приключений

                                                • 0
                                                  Создавать файлы, которые будут эмулировать хедеры, перекладывать это на шею ide?

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

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

                                                  Далее, дистрибуция. Это опять приключения. Нужна статическая линковка, либо приключения с контейнерами.

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

                                                  Если и о компиляторах думать не хочется, можно распространять только исходники и собирать их самостоятельно в модули, в любом случае соберутся быстрее старого подхода. header-only библиотека ортогональна простоте сборки: у неё точно так же могут быть зависимости, которые потребуется собрать, у неё могут особые требования на флаги компиляции, которые желательно носить рядом, в том же cmake. Хорошие header-only либы этими проблемами не страдают, ну так и модули можно писать точно так же, избегая сложных зависимостей и сложной сборки.
                                                  • 0
                                                    Почему бы и нет? Либо просто показывать интерфейс в менюшке автокомплита, как это делается во многих ide сейчас?

                                                    Интерфейс мало кому нужен, к тому же было проигнорировано множество тезисов, как и множество деталей.

                                                    В подсказке описание типа/класса тоже можно показать? Да и сигнатуру функции, особенно с какими-то концептами, контрактами и прочим. Где это всё показывать? И в каком виде? В виде кода?

                                                    Опять же, куда девать коменты — нужно ещё и это эмулировать.

                                                    Так же, я говорил не только о сигнатурах, но и о самих исходниках, которые всегда и удобно доступны в ситуации использования header-only.

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

                                                    Проблема в том, что в ситуации с header-only думать не нужно. И пока это лишь фантазии, а я сравнивал реальность. Я утверждал, что модули дают профит по части кросскомпиляции и задавал вопрос, а дадут ли тот же профит модули? Я не сравнивал модули и header-only. Был вероломно подменён тезис, что очень некрасиво.

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

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

                                                    header-only библиотека ортогональна простоте сборки

                                                    Header-only предполагает header-only, и это очевидно. Если header-only тянет какие-то левые бинарные зависимости — это уже не header-only.

                                                    у неё точно так же могут быть зависимости, которые потребуется собрать

                                                    Header-only зависимости не нужно собирать.

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

                                                    Флаги компиляции чего? У меня такое чувство, что вы мало знакомы с темой, очень мало. Header-only это встраиваемая зависимость, она не собирается — она собирается в контексте вашего кода. Для неё не может быть каких-то отдельных флагов для cmake — они будут накладываться на всё то, куда вы это заинклюдили. И если это «куда» является так же header-only, то инклюдится это ТОЛЬКО в ОДНО место, а значит флаги всегда ОДНИ.

                                                    К тому же, у стандартного кода может быть только один флаг — это std, который совместим сверху вниз, а значит в любом случае код использующий header-only зависимость нужно собирать с std не ниже необходимого для зависимости.

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

                                                    Модули — это уже лишние телодвижения. При этом уже нужна какая-то система сборки, уже нужно будет делать всё тоже, что и
                                                    при header-only, только с куда большим набором заморочек. При этом просто в голословное «можно» я поверить не могу.

                                                    Модули для леги кода? Да. Модули для блобов? Да. Преимущества очевидны, а вот в сравнение с современными техниками — преимущества очень не очевидны, по крайней мере мне.
                                                    • +2
                                                      У меня такое чувство, что вы мало знакомы с темой, очень мало.

                                                      Может, не стоит скатываться в обвинения о непрофессионализме? Это оскорбительно и лишь провоцирует на взаимные обвинения.


                                                      Давайте лучше почитаем proposals по модулям и предположим, как эти предложения смогут реализовать компиляторы. А для начала договоримся об одном понимании header-only библиотек, какие "гарантии" они дают, а какие — нет.


                                                      К тому же, у стандартного кода может быть только один флаг — это std, который совместим сверху вниз, а значит в любом случае код использующий header-only зависимость нужно собирать с std не ниже необходимого для зависимости.

                                                      Предположим, я пишу проект на последнем C++17 и подключаю к нему header-only библиотеку, которая:


                                                      • Использует в своей реализации старенький std::bind1st. Эта вещь была удалена в 17 плюсах. Gcc спокойно скомпилирует заинклюженную в мои cpp файлы эту header-only библиотеку. Clang тоже. Но только если будет использоваться std либа от gcc. Если я подключу std от clang, то получу ошибку — там эта вещь была удалена в соответствии со стандартом. Флаг std не совместим сверху вниз, в нашем примере он навязывается проекту от header-only либы, иначе проект не соберется только из-за несборки кода header-only библиотеки.
                                                      • Использует чуть более современный std::thread. Чтобы мой проект собрался с этой header-only либой на линуксе, я должен слинковаться с pthread.
                                                      • Использует std::filesystem. Тогда на компиляторе gcc я должен слинковаться с stdc++fs.
                                                        Вывод: header-only библиотека может зависеть от бинарей даже в случае, если используется только стандартный c++.

                                                      А теперь о том, какие же "гарантии" дает header-only либа. А она гарантирует лишь:


                                                      • что все необходимые файлы с реализацией будут автоматически подключены в конечный cpp файл;
                                                      • если какая-то часть библиотеки не используется в конечных cpp файлах, она туда не подключается и соответственно не собирается, ускоряя сборку;

                                                      А так же гарантирует минусы:


                                                      • включая в себя все необходимые файлы с реализацией, соответствующий заголовок при включении в конечные cpp файлы будут требовать разбора/инстанцирования/компиляции одного и того же кода раз за разом в каждом cpp файле.
                                                      • Сделает видимым конечному потребителю все детали реализации, давая возможность заиспользовать то, что не должно быть использовано напрямую.

                                                      Способ, которым компилятор ищет подключаемые заголовочные файлы — implementation defined. В простом, но очень некрасивом случае, подключаемую библиотеку можно положить рядом с своим исходным кодом и она будет найдена при инклюде без лишних телодвижений. В общем же случае необходимо передать компилятору с помощью директивы директории, где искать подключаемые заголовки.


                                                      И почему вообще header-only библиотеки вы называете "современным" подходом? Что в этом современного? Его разве изобрели пару лет назад?


                                                      Неужели эти "гарантии" не смогут предоставить модули?
                                                      Способ поиска модуля при импорте — implementation defined. Компилятор точно так же может сделать директиву, где будет принимать директорию для поиска подключаемых модулей.
                                                      В случае, если будет принято предложение ATOM об объявлении модуля только на верху файла, тогда компилятору будет достаточно прочесть заголовки всех файлов в этой директории, чтобы понять, что потребует сборки. Это не так быстро, как чтение хедера по прямому пути из инклюда, но выполняется однократно, в отличии от многократного чтения хедера при каждом включении. Если предложении от ATOM не будет принято, компилятору придется прочесть/распарсить каждый файл целиком, чтобы найти там объявления модуля. Это соответственно дольше.
                                                      Всё! Гарантии header-only либы могут быть соблюдены и модулями! При этом они лишены недостатоков header-only либы — вся внутренняя реализация библиотеки не будет видна конечному коду и будет компилироваться только один раз.


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

                                                      • +1
                                                        Использует в своей реализации старенький std::bind1st. Эта вещь была удалена в 17 плюсах. Gcc спокойно скомпилирует заинклюженную в мои cpp файлы эту header-only библиотеку. Clang тоже. Но только если будет использоваться std либа от gcc. Если я подключу std от clang, то получу ошибку — там эта вещь была удалена в соответствии со стандартом. Флаг std не совместим сверху вниз, в нашем примере он навязывается проекту от header-only либы, иначе проект не соберется только из-за несборки кода header-only библиотеки.

                                                        Вы утверждали, что нужны какие-то флаги(отдельные) отдельно для header-only, на что я вам ответил — никаких отдельных флагов нет, а всё, что может наложить header-only на ваш код — это std.

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

                                                        По поводу «навязывается» — я уже об этом говорил, в любом случае он навязывается.

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

                                                        Использует чуть более современный std::thread. Чтобы мой проект собрался с этой header-only либой на линуксе, я должен слинковаться с pthread.

                                                        Будь она какой угодно — вам нужно будет линковаться с pthread.

                                                        Вывод: header-only библиотека может зависеть от бинарей даже в случае, если используется только стандартный c++.

                                                        Очевидно, что стандартные зависимости мало кого волнуют, ведь они стандартные. О них никто и не говорил, их никто не учитывает. Если что-то требует с++17 — обеспечьте это. -pthread, -lstdc++fs — это всё заморочки конкретного компилятора/платформы.

                                                        К тому же, я всё это заранее проговаривал:

                                                        Если header-only тянет какие-то левые бинарные зависимости — это уже не header-only.

                                                        Я уточнял и про левые зависимости, и про то, что header-only может их тянуть, и про то, что я не считаю подобное header-only.

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

                                                        Это не проблема, организуйте нормальную(модульную) сборку. К тому же, ничего не мешает организовать в проекте подобное и без сборки. Разделите проект на header-only модули. Он будет работать и собираться быстрее.

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

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

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

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

                                                        Способ, которым компилятор ищет подключаемые заголовочные файлы — implementation defined. В простом, но очень некрасивом случае, подключаемую библиотеку можно положить рядом с своим исходным кодом и она будет найдена при инклюде без лишних телодвижений. В общем же случае необходимо передать компилятору с помощью директивы директории, где искать подключаемые заголовки.

                                                        Такая же придирка, как и в случае выше. Простой пример /usr/include? Инклюды обладают очень простым свойством — их можно накидать куда угодно и использовать, язык это умеет.

                                                        И если что-то и нужно передавать — это корневой каталог по типу /usr/include и не более того. И даже если это какая-то сложная иерархия, то они организуется из любого thrid_payty без каких-либо проблем.

                                                        В сравнении с тем, что нужно сделать в ситуации со сборкой — это 1 против 100.

                                                        И почему вообще header-only библиотеки вы называете «современным» подходом? Что в этом современного? Его разве изобрели пару лет назад?

                                                        Изобрели их дано, массовым явление стало достаточно недавно.

                                                        В современных реалиях «старый» подход оправдан очень в малом кол-ве случаев. И в противовес старому подходу я называю header-only новым подходом.

                                                        Неужели эти «гарантии» не смогут предоставить модули?
                                                        Способ поиска модуля при импорте — implementation defined.

                                                        Если бы они сделали инкрементальную сборку, то это да — даст очень большой профит. Что ещё даст какой-то профит — я не знаю.

                                                        В случае, если будет принято предложение ATOM об объявлении модуля только на верху файла, тогда компилятору будет достаточно прочесть заголовки всех файлов в этой директории, чтобы понять, что потребует сборки. Это не так быстро, как чтение хедера по прямому пути из инклюда, но выполняется однократно, в отличии от многократного чтения хедера при каждом включении.

                                                        Меня не особо радуют всякие ограничения про «вверху файла». К тому же, одно дело «может», а другое дело «делает».

                                                        Всё! Гарантии header-only либы могут быть соблюдены и модулями! При этом они лишены недостатоков header-only либы — вся внутренняя реализация библиотеки не будет видна конечному коду и будет компилироваться только один раз.

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

                                                        Опять же, «внутренняя реализация видна» — для меня является не минусом, а плюсом. Никакого минуса я тут не вижу вообще.

                                                        По поводу «компилироваться» — это недостаток по части оптимизации. Это будет очередная линковка. Если же это не линковка, то компиляция будет, а модуль будет просто частично скомпилирован( и то, лишь в теории).

                                                        • 0
                                                          Видимо, мы друг друга не убедим до тех пор, пока не увидим, как implementation defined детали работы с модулями будут реализованы в компиляторах.
                                          • 0
                                            Хм, мне всегда казалось, что компоновка в этом аспекте не уступает компиляции, а часто компиляцию превосходит. Нет?
                                  • 0
                                    Или у них появятся форки, в которых макросы заменены на constexpr силами людей, у которых ещё есть шило в одном месте…
                                    В конце концов, столько времени потратить на то, чтобы минимизировать влияние препроцессора на время компиляции, и опять мы суём макросы, чтобы увеличить влияние препроцессора на время компиляции… зачем?
                            • +1
                              Модулей нет, зато есть куча всякого ада. А еще нет вирутальной шаблонной функции. Просто браво…
                              • +2
                                Ну это кому как. Я контракты второй год с нетерпением жду, а std::bit_cast люди хотели ещё лет 20 назад…

                                Ну и не стоит так надеяться на модули, в 10 раз они компиляцию не ускорят (в ближайшее время).
                                • 0
                                  Ускорят. Просто надо дать время компиляторописателям, чтобы они это написали. А так… пока в Стандарт не приняли, некуда и спешить.
                                  • +1
                                    Ускорят.
                                    Боюсь, будет сильно зависеть от кода. По опыту работы с некоторыми header-only библиотеками получается, что иногда львиная доля времени во время компиляции уходит на инстанцирование и оптимизацию шаблонов, а не на загрузку и обработку заголовочных файлов.
                                    • 0
                                      Modules TS предлагает по-максимому расшаривать explicit и implict инстанцирования, идущие с модулем.
                                  • 0
                                    Контракты — это очень хорошие новости. Я правильно понимаю, что для них ещё нет имплементации ни в gcc ни в clang ни вообще нигде, даже прототипа?
                                    • +1
                                      Увы, пока нет. Но в контрактах очень заинтересованы крупные компании, скорее всего имплементация появятся в скором времени.
                                • +1
                                  constexpr я еще года 4 назад хотел для себя в regexp(я использую другую реализацию с опенсорса)
                                  но не в качестве оптимизации стейт машины, а в качестве скрытия что программа использует regexp и скрытия самого текстового regexp которым парсится
                                  и ровно сегодня опять заглядывал в код regexp'а и думал как бы его реализовать…
                                  • +2
                                    Думаю вам пока подойдёт вот эта библиотека. Есть её видео презентация от автора.

                                    Пример использования:
                                    constexpr auto re = "^(?:[abc]|xyz).+$"_pre;
                                    re.match(some_string);
                                    
                                    • 0
                                      ну не, там уж слишком закрутила девчуха
                                      я наверное не правильно выразился, я тоже хотел только рекегксп строку в массив стейт машины перевести
                                      там только проблема массива на выходе который динамический по длине
                                      если вынести его как то наружу то думаю стд аррей какой нибудь может и сойдет
                                      • +1
                                        Offtop: кстати, девушка с видео есть на фотке из поста
                                        Offtop^2: главный нелюбитель фоток (Бьярни) спрятался за мной, пока я фоткал фотографа.
                                  • +2
                                    Логика подсказывает, что если у нас есть constexpr std::regex значит у нас есть «сделай из строки что угодно на этапе компиляции», т.е. фактически те же макросы что в языке D. Это конечно породит большой поток ненависти от некоторых, но мне очень даже нравится, т.к. намного проще инжектировать прямо в компилятор строки нежели пытаться использовать API самого компилятора, строить там ООП структуры через специальный API, все такое. Это, если хотите, «метапрограммирование для чайников».
                                    • +3
                                      Ждём настоящего «нормального» метапрограммирования с приходом Reflection в язык. Вот тогда уже можно будет делать очень много интересных вещей (а если ещё и скрестить с металкассами...)
                                      • 0
                                        Вангую что с метаклассами мы получим:
                                        * генерацию парсеров из EBNF и других форматов описания грамматик
                                        * ORM классов из коробки
                                        * сигнал/слоты Qt без макросов
                                        * расцвет DSL
                                        * красивый RPC
                                        * ???
                                        • +1
                                          сигнал/слоты Qt без макросов

                                          github.com/woboq/verdigris
                                          У автора в блоге (https://woboq.com/blog/), кстати, много всего интересного написано про нестандартное использование C++.
                                          • +1
                                            Но ведь по ссылке с макросами… :(
                                            • 0
                                              Да, точно. Имел ввиду, что без MOC.
                                          • +1
                                            А про сами метаклассы что-нибудь слышно, кстати?
                                            • +1
                                              Метаклассы базируются на рефлексии, рефлексия требует constexpr контейнеров и алгоритмов.

                                              Constexpr алгоритмы мы дотолкали до стандарта. Надеемся увидеть в C++20 constexpr контейнеры; рефлексию — в C++23. Где-то в это время и появятся метаклассы в стандарте.
                                              • +1
                                                Спасибо!
                                                Надеюсь, доживем :)
                                            • 0
                                              Практика показывает, что для ORM достаточно BOOST_FUSION_ADAPT_STRUCT, хотя в итоге выглядит все убого и компилируется медленно. Интересно, насколько метаклассы/рефлексия это улучшит.
                                              • 0
                                                Парсеры на шаблонах: github.com/taocpp/PEGTL
                                                В принципе почти хватает и сейчас, без метапрограммирования
                                          • +1
                                            constexpr!
                                            Я вместо if(!result) пишу if (result == false), ибо восклицательный знак легко теряется из виду.
                                            Может стоит дать другое имя?
                                            И будут ли весь math модуль делать constexpr? Мне бы не помешал constexpr синус и косинус.
                                            Да и может быть стоило бы всё-таки сделать какой-нибудь «std2»?
                                            • 0
                                              А какое имя вы предлагаете?
                                              • +6
                                                да даже и не знаю. Возможно, какой-нибудь static_expr или compilet. Сходу так не скажешь. Но наличие восклицательного знака однозначно напрягает. В C++ нет ни одного ключевого слова с восклицательным знаком. Зачем нарушать эту тенденцию?

                                                Ещё одно занятное изменение, которое рассматривается для приёма в C++20 — это constexpr! функции.

                                                Я когда прочитал это предложение, то не понял в тот же миг, что оно говорит о чем-то новом)
                                                • 0
                                                  Я вообще когда только начал знакомиться с constexpr, был крайне раздосадован, что они как раз не ведут себя «всегда constexpr!»., если бы можно было вернуть время вспять, я бы предложил constexpr и maybe_constexpr какой-нибудь (соответствующий сегодняшему)
                                                  • +1
                                                    абсолютно с вами согласен
                                              • +2
                                                Поддерживаю, делать настолько похожие ключевые слова — не лучшая идея.
                                                Я уж молчу про то, как легко слово «констекспр» произносится вслух -_-' Но говорить вслух «констекспр!» будет вообще невозможно.
                                                «Констекспрбанг»? «Констэкспр экскламейшн марк»? «Констэкспр восклицательный знак»?

                                                Гуглить тоже очень тяжело будет.
                                              • +1
                                                В вашем примере восклицательный знак «зажат» между скобкой и идентификатором, от чего плохо читается (например, я бы предпочел такое условие обособить). За обсуждаемой конструкцией почти всегда будет следовать пробел.
                                                • 0
                                                  И всё-таки, насчёт библиотеки: constexpr(2011), концепты, метаклассы, модули. Чем не повод сделать абсолютно новую и чистую либу, которой будет легко и приятно пользоваться, которая не будет страдать от миллиона проблем?
                                                  Это всё мне напоминает чемодан, который с каждым годом становится всё дырявее и дырявее, а его выкинуть жалко, поэтому дырки латают и красят новым модным цветом.
                                                  Да все контейнеры (также их итераторы) и алгоритмы будут гораздо проще и выразительнее с концептами и с учётом существования compile-time выражений почти всё можно переписать и сделать чище.
                                                  При этом всём не нужно выкидывать старую библиотеку (дабы всеми необходимая обратная совместимость осталась). Но и развивать старую библиотеку я не вижу смысла.
                                                  Такой потенциал теряем…
                                                  • 0
                                                    А что бы вы хотели такого сделать в стандартной библиотеке, что поломает обратную совместимость? Концепты и контракты можно добавлять к уже имеющимся классам стандартной библиотеки, и всё будет OK.
                                                    • +2
                                                      Всё. Нужно позволить себе провести тотальный рефакторинг, с учётом появления супер-плюшек, появившихся за последние 16 лет и не бояться потерять обратную совместимость. Со спокойной душой пересмотреть большинство концепций и сделать библиотеку изначально заложив туда свежий фундамент, а не достраивать этажи поверх старого и не прочного фундамента.
                                                      Я понимаю, что я как-то сегодня прилично так умничаю, но просто мне кажется, что для молодых специалистов (как и я) был бы C++ более привлекателен с подобной новой и чистой библиотекой.
                                                      Недавно в теме по std::visit происходили серьёзные стычки между сообществом C++ и сообществом Rust. Аргументы Rust-овщиков были вполне убедительными, поскольку Rust — это молодой язык, который создавался опираясь на горький опыт своих предшественников.
                                                      А тут такой шанс по новому взглянуть на мир в пределах плюсов, с новыми возможностями и не заменять старую библиотеку и не приклеивать новые возможности к старым с помощью скотча, а сделать всё сразу и хорошо.
                                                      Ну ведь сейчас так и происходит, что все новые фичи прикручивают к старым. Тот же constexpr почти по всей библиотеке можно прилепить. Но это ведь в корне может поменять все концепции библиотеки.
                                                      Концепты, модули и метаклассы — это через чур глобальные изменения для того, чтобы быть использованными поверх старых наработок. Необходимо использовать сей потенциал. Иначе, всё-равно все станут писать свои велосипеды. Так и происходит сейчас даже. constexpr std::swap мне приходилось писать свой, абсолютно такой же, но с constexpr. Вы продвигаете constexpr std::regex. Контейнеров новые штуки также касаются. Итераторы будут безумно изящнее с концептами. Подобных примеров уже существует уйма. Библиотека std простаивает.
                                                      • 0
                                                        Мне кажется, если молодому специалисту объяснить, что «безопасное» подмножество C++ — это то, что соответствует Core C++ Guidelines, а все остальное не рекомендовано и должно быть инкапсулировано, ему станет гораздо легче жить. А огромное количество старых вещей в Core C++ Guidelines запрещены или разрешены лишь для короткого списка исключений.

                                                        Да, такой безопасности, как в Rust, не будет. Но будет гораздо лучше, чем раньше.
                                                        • 0
                                                          О, я вас узнал сразу)
                                                          Но я же говорю не о безопасности, а обо всей библиотеке в целом. Да её можно сделать хоть смешав с GSL.
                                                          Факт в том, что на базе новых новшеств и базируясь на том, что язык за последние 20 лет тотально прокачался, нужно пересмотреть стандартную библиотеку вдоль и поперек. А старую оставить доживать ради обратной совместимости и всё.
                                                          Если сейчас посмотреть предложения на stdcpp.ru, то там большая часть предложений начинается со словосочетания «добавить constexpr к ...». Появляются модули, концепты и метаклассы. Это приведет к чему? К тому же самому, что происходит с constexpr. При этом модифицируя существующие сущности, которые уже много раз итак модифицировались и базировались совсем на других вещах, комитет получает головную боль, поскольку нужно все сделать аккуратно и нигде ничего не сломать и не переименовать и т.д. и т.п.
                                                          Также, что-то теряет свой смысл и со временем выбрасывается. Но это всё вытекает в то, что библиотека растянет на долгие годы (ибо стандарт выходит раз в 3 года) своё эволюционирование и в итоге придёт к тому же самому, что можно сделать здесь и сейчас и в разы проще.
                                                          • +2
                                                            Есть история с Python3, которая пугает всех в комитете.

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

                                                            А главное — в чём плюсы то? Пока что, всё что хочется, все новые фишки, можно добавлять к стандартной библиотеке не ломая обратную совместимость.
                                                            • 0
                                                              Если мы сделаем std2, то как заставить пользователей на него быстро переехать? Если переезжать долго, то придётся десяток лет развивать две версии стандартной библиотеки, получать лучи ненависти от людей, вынужденных мигрировать на новую версию библиотеки…

                                                              Комитет ведь что-то сподвигло сделать C++ Core Guidelines и GSL.
                                                              И я подразумеваю то, что развитие старой библиотеки прекращается. Новая в себя будет включать идентичные сущности, но без мусора, в адекватном и чистом виде.
                                                              Я не настаиваю на том, что я абсолютно прав и нужно сделать так как я говорю) Не мне решать.

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

                                                              Как по мне, «пока что» — это до C++20. Уж слишком масштабное обновление будет.
                                                              Согласитесь, что constexpr уже сместил сильно с места стандартную библиотеку, о чём я выше написал.
                                                              Я вижу библиотеку сейчас, как фундамент, который не был изначально рассчитан на тот груз, который на нём стоит. И со временем любое строение изнашивается и становиться более хрупким, тем-более если сверху достроить пару этажей и дополнительные балкончики, а также кондиционеры навешать)
                                                              • +4
                                                                Аналогии почти всегда неверны. Если сейчас поломать либу, то никто на неё просто не будет переходить и получится разделение как с Python 2/3, только С++ before 20 и C++ 20 и выше. Такое себе, лично я считаю.
                                                            • 0
                                                              Ну вот как раз сейчас любой желающий может сделать такую библиотеку для себя, смешав с GSL по вкусу, поэтому проблема не сказать, чтобы была очень острой, на мой взгляд. А второго Python3 нам не надо.
                                                          • 0
                                                            Странно записывать std::visit во что-то древнее, ему ведь всего 1 годик :)

                                                            Так что конкретно вы хотите изменить в стандартной библиотеке? Какие основы хотите пересмотреть?
                                                            • 0
                                                              Странно записывать std::visit во что-то древнее, ему ведь всего 1 годик :)

                                                              Там весь конфликт был не вокруг std::visit, а вокруг того, что Rust-овщики пришли прогонять C++ сообщество со своими правильными умными указателями)
                                                              • 0
                                                                Кто же спорит — указатели в Rust самые правильные!

                                                                Жаль только что пользоваться ими не удобно, оттого и серьёзных програм на нём нет.</troll-mode>
                                                                • 0
                                                                  Я не буду с вами спорить и я сам тремя руками за C++.
                                                                  Просто в том споре мы знатно присели, оправдывая себя «обратной совместимостью» и возрастом.
                                                                  Уж слишком убедительные и настырные противники были.
                                                                  Но я вот совсем не планирую уходить на Rust, поскольку душа у меня к плюсам тянется.
                                                                  Также я не требую «такие плюшки как в Rust». Я сейчас о другом говорил всё время. О том, что уже есть, но под новым углом.
                                                                  • 0
                                                                    Пусть растоманы пишут что хотят, вам то какая разница? Каждый выбирает средство сам. Не хотят растоманы поддерживать тонны уже написанного кода и написать с нуля? Пожалуйста, кто ж против.
                                                                  • 0
                                                                    Вот я же не первый, кто пишет об «std2». Это периодически мелькает перед глазами:
                                                                    вот
                                                                    и
                                                                    вот
                                                                    и я искренне надеялся на то, что так и будет с C++20.
                                                                    Вы ведь и сами желайте всё то, что есть модифицировать, но с учётом обратной совместимости вам это обходиться сложнее, чем могло быть. Мне кажется, что с 20-ым стандартом всё станет еще в пять раз сложнее.
                                                                    Назревает новая эпоха, а библиотека Modern C++ всё еще не Modern.
                                                                    • +2
                                                                      Я видел на ютубе видео с конференции, где поднимали тему std2, нужна ли она, в каком виде, но названия видео не вспомню. Нашел только презентацию от одного из участников той дискуссии: www.youtube.com/watch?v=fjtwfauk7a8.
                                                                      • 0
                                                                        Да. Абсолютно тот же мотив.
                                                                        • +1
                                                                          Да, я там был. И могу рассказать о настроениях в зале — скептическое.
                                                                        • +2
                                                                          Очень надеюсь, что такой ломающей совместимости никогда не будет в Стандартной библиотеке. И в любом треде, где это будет подниматься, я буду топить исключительно против.
                                                                    • 0
                                                                      Нет, там конфликт был из-за того, что один Rust-оман, плохо знающий C++, подумал, что unique_ptr и shared_ptr используются в C++ для тех же целей, для чего родные ссылки в Rust-е. И пришлось потратить кучу времени, чтобы понять это :(

                                                                      Ну а так, конечно, std::visit и std::variant по сравнению с полноценным паттерн-матчингом и алгебраическими типами данных выглядит скромненько. Только вот в обозримом времени АлгТД в C++ вряд ли завезут. Если бы завезли, то в C++ было бы проще переиспользовать полезные практики из других современных языков.
                                                                  • +2
                                                                    ИМХО, проще тогда уже сделать новый язык, который по синтаксису, идеологии и каким-то основным приемам будет настолько сильно напоминать C++, что при желании можно будет брать имеющийся C++ный код, править его на 20-30% и получать код на новом, более современном, удобном и безопасном языке. Заодно можно было бы в приказном порядке закрыть проблемы с отсутствием единой системы сборки и общей системы управления зависимостями.

                                                                    Ценность же самого C++ в том, что сохраняется совместимость с древним кодом, который написали и отладили давным-давно, но который все еще работает. У меня самого были в практике случаи, когда доставался старый код, написанный и заброшенный к тому времени уже лет 8 как, просто компилировался новым компилятором и шел в дело. Или когда старый код, который эволюционировал около 10 лет в рамках C++98 за пару вечеров был переведен под C++11. Потребовалось в ряде мест поменять std::auto_ptr на std::unique_ptr и еще что-то по мелочи. Или когда узнаешь, что где-то работает твой код, который был написан еще году в 2008-ом, и в котором были переиспользованы куски кода, написанного в 1998-ом.
                                                                    • +1
                                                                      ИМХО, проще тогда уже сделать новый язык, который по синтаксису, идеологии и каким-то основным приемам будет настолько сильно напоминать C++

                                                                      Не соглашусь и уже далеко не одна подобная попытка была.
                                                                      Стоит всё-таки разделить язык и его стандартную библиотеку.
                                                                      Сам язык позволяет написать новую «идеальную» библиотеку, так что нет необходимости делать новый тысячный ЯП.
                                                                      Все ключевые изменения 20-ого стандарта относятся строго к языку, а не к библиотеке, в том то и дело. И всё то, о чём я здесь так много писал, относиться только к библиотеке.
                                                                      • 0
                                                                        Не соглашусь и уже далеко не одна подобная попытка была.
                                                                        А вот я не помню ни одной заметной попытки, где хотели бы оставить синтаксис и идеологию максимально близкой к C++.

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

                                                                        В Rust-е и синтаксис другой, и borrow-checker с lifetimes заставляют по другому думать. И объектная модель совсем другая. Так что переделать C++ный код в Rust — это даже больше, чем переписать, тут еще и перепроектировать потребуется.

                                                                        Что еще?
                                                              • 0
                                                                Открыл для себя наличие ключевых слов not, or, and в C++ и забыл о крипто-синтаксисе !, &&, ||, хотя последние два еще читаемые, но вот тоненький восклицательный знак почти незаметен. В новом коде самое то.
                                                              • 0
                                                                Обсуждали что-нибудь о сайд-эффектах в контрактах?
                                                                • 0
                                                                  Пока что, любой side effect является Undefined Behavior. Скорее всего так и останется.
                                                                  • 0
                                                                    Есть желание (которое я не разделяю, но могу понять) использовать printf/cout-style debugging внутри контрактов. Собственно, у меня лично рождается что-то в районе двух вариантов:
                                                                    1. Что-то, аналогичное elision сайд-эффектов при copy elision. То есть, реализация, условно, обязуется вполнить сайд-эффекты, но только если она вообще скомпилирует соответствующую проверку.
                                                                    2. Перенос этого на инструменты с предоставлением функций вроде std::contract_log_values, std::contract_log_string и т. д., семантика которых компилятору известна.

                                                                    Но это всё очень плохо пахнет, просто, имхо, UB здесь пахнет ещё хуже.
                                                                    • 0
                                                                      вам о возможном нарушении контракта скажет компилятор еще на этапе сборки.
                                                                      • 0
                                                                        Несомненно, такой режим обработки контрактов можно реализовать, но мне почему-то кажется, что это будет практически не очень оправдано.
                                                                        • +2
                                                                          контракты — механизм для уменьшения числа проверок в рантайме к минимуму (плюс бонусом дополнение к документации). Вы предлагаете разменять доп. быстродействие на вывод в консоль? На мой взгляд, это мало чем лучше ассертов.
                                                                          • +1
                                                                            A contract is checked (or not) at run time depending on a build system setting. // p0380r1

                                                                            Контракты — это не зависимые типы с эрейзом после тайпчекинга (увы).
                                                                            • 0
                                                                              Ну вы почитайте дальше, в секциях 2 и 3 подробно описано что это за «build system setting»: вы собираете в одном из режимов: «без runtime проверок», либо «только дешевые проверки» и «все проверки». Первое предназначено для релизных сборок, а второе и третье — отладочных

                                                                              Я имел в виду с++ контракты. Впрочем, они полностью подходят под общепринятое понятие контракта.
                                                                              • +1
                                                                                Так от того, что вы проверки в рантайме отключите, они у вас в компилтайм магическим образом не перейдут (иначе как, цитируя вас, компилятор что-то мне скажет ещё на этапе сборки?).

                                                                                Кроме того, я не очень понимаю, с чего вы взяли, что я предлагаю разменивать быстродействие на вывод в консоль. Ровно из-за этого и возникает искомая проблема: контракты по своей сути опциональны к «исполнению» в смысле компиляции их тела в какие-то рантайм-проверки, а делать сайд-эффекты опциональными может быть неприятно. Вопрос: что делать с сайд-эффектами.
                                                                                • +1
                                                                                  Не делать их :)
                                                                                  • 0
                                                                                    Так люди хотят!

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

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

                                                                                    Но ещё лучше, конечно, да, не делать их вообще, тут я с вами согласен.
                                                                                    • 0
                                                                                      Там во многих местах при работе контрактов нарочно поставлены UB.

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

                                                                                      А до тех пор, комитет решил выпустить контракты в минимальном виде. Виде, который подходит для использования в стандартной библиотеке и для использования в кодовых базах ряда крупных компаний.
                                                                                      • 0
                                                                                        В принципе, да, разумно, UB — наиболее широкая спецификация поведения, которую легко потом уточнять.
                                                                                        • +1
                                                                                          т.е. на данный момент контракты — это просто doxygen-friendly assert и ничего более?
                                                                                      • 0
                                                                                        А кто будет гарантировать, что тело контракта — это pure function? C++ компиляторы такое осилят?
                                                                                        • 0
                                                                                          Для очень простых случаев могут, при желании. Собственно, поэтому речь о том, чтобы сказать, что программа с IO в контрактах — ill-formed, no diagnostic required. UB, то бишь.
                                                                                          • 0
                                                                                            Именно, я хотел сказать, что «не делать их» — это плохой совет т.к. нет возможности гарантировать их отсутствие, следовательно рассчитывать на то, что сайд-эффектов не будет — очень ненадёжно. Это практически гарантирует UB время от времени т.к. человек не может не делать ошибок, а тут их сделать очень легко, а найти — может быть очень трудно.
                                                                                            • 0
                                                                                              Вы так говорите, будто такое первый раз в плюсах :)
                                                                                              • 0
                                                                                                Ну более error-prone механизм не припомню. Обычно UB можно избежать следуя довольно простым правилам.
                                                                                          • 0
                                                                                            компиляторы времен царя гороха поддерживают аттрибуты типа pure/const
                                                                                            • 0
                                                                                              const — это не pure. Какой C++ компилятор поддерживает pure?
                                                                                              • 0
                                                                                                • 0
                                                                                                  они позволяют объявить что-то pure, но я не вижу, что бы компилятор мог вывести pure для функции.
                                                                                                  float twice(float x) __attribute__ ((pure)) 
                                                                                                  { return x*2.0f;}
                                                                                                  float fourtimes(float y) 
                                                                                                  { return twice(twice(y));}

                                                                                                  Компилятор не узнает, что fourtimes — pure.
                                                                                                  • 0
                                                                                                    GCC должен уметь такое выводить. Попробуйте перепроверить, где-то на уровне IR должна выставиться инфа о том что функция pure.
                                                                                                    • 0
                                                                                                      тогда почему purity не вводится в стандарт вместе с контрактами и не требуется для контрактов, а вместо этого нарушение purity объявляется как UB?
                                                                                                      • 0
                                                                                                        Разные компиляторы умеют выводить эти атрибуты в разной степени. Думаю вы не будете рады, если у вас код с контрактами соберётся на одном компиляторе, а на другом будет говорить «error, not pure»
                                                                                                        • 0
                                                                                                          именно, потому нет способов гарантировать отсутствие side effects, но приходится требовать его, что точно приведёт к UB, т.к. для человека гарантировать отсутствие side effects на практике нереально, слишком нетривиальная задача.