Работа в комитете по стандартизации языка C++ активно кипит. Недавно состоялось очередное заседание. Как один из участников, поделюсь сегодня с Хабром свежими новостями и описанием изменений, которые планируются в С++26.

До нового стандарта C++ остаётся чуть больше года, и вот некоторые новинки, которые попали в черновик стандарта за последние две встречи:

  • запрет возврата из функции ссылок на временное значение,
  • [[indeterminate]] и уменьшение количества Undefined Behavior,
  • диагностика при =delete;,
  • арифметика насыщения,
  • линейная алгебра (да-да! BLAS и немного LAPACK),
  • индексирование variadic-параметров и шаблонов ...[42],
  • вменяемый assert(...),
  • и другие приятные мелочи.

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



Запрет возврата из функции ссылок на временное значение


Благодаря предложению P2748, компилятор C++26 не позволит вам сформировать ссылку на временное значение, созданное в return:

const int& f1() {
    return 42;  // ошибка
}

Подобные ошибки весьма разнообразны и не всегда их легко обнаружить при беглом взгляде:

#include <map>
#include <string>

struct Y {
    std::map<std::string, int> d_map;

    const std::pair<std::string, int>& first() const {
        return *d_map.begin();  // тут возвращается std::pair<CONST std::string, int>
    }
};

При этом встроенные в современные компиляторы механизмы предупреждений зачастую могут диагностировать ещё больше неправильных использований и висящих ссылок. Так что аналоги флагов -Wall и -Wextra остаются вашими друзьями и дальше. Надёжные диагностики без ложных срабатываний продолжат потихоньку переходить в стандарт C++.

[[indeterminate]] и уменьшение Undefined Behavior


Продолжаем тему с безопасностью. В P2795 ввели понятие erroneous behavior — поведение, которое рекомендуется диагностировать компиляторам, которое запрещено в constexpr-контекстах и которое является последствием ошибки в коде. Таким поведением сделали работу с неинициализированной переменной, например:

void fill(int&);

void sample() {
  int x;
  fill(x);     // ошибочное поведение (erroneous behavior)
}

Поведение компилятора при erroneous behavior определено. Другими словами, комитет идёт к уменьшению количества undefined Behavior в C++, чётко описывая, что происходит в том или ином случае, чтобы мотивировать компиляторы диагностировать подобные ошибки и при этом не увеличивать время выполнения приложения.

Что подводит нас к новому атрибуту [[indeterminate]]. Если у нас есть неинициализированная переменная и есть функция, которая только пишет в переменную, то можно компилятору дать подсказку, что это не ошибочное поведение. Тогда значение из переменной не будут читать в функции:

void fill(int&);

void sample() {
  int x [[indeterminate]];  // без атрибута компилятор выдаст предупреждение
  fill(x);     // всё в полном порядке
}

Ошибочное поведение — это не ошибка компиляции, а предупреждение от компилятора! Фактически, многие компиляторы уже предупреждают в этом случае.

Диагностика при =delete;


Как-то раз во время обсуждения уже не помню какого предложения (кажется [[nodiscard("should have a reason")]]), мы в международной группе пришли к такой мысли: «А было бы неплохо выдавать произвольную диагностику и для =delete». Лично нам в Яндексе это очень бы пригодилось для фреймворка ? userver, чтобы вместо вот такого длинного кода можно было просто написать:

  const T& operator*() const& { return *Get(); }
  const T& operator*() && =delete("Don't use temporary ReadablePtr, store it to a variable");

И теперь с принятием P2573 в C++26 можно прописывать диагностические сообщения прямо в =delete.

Pack indexing


Если вы часто пользуетесь variadic templates, то вы, скорее всего, настрадались с Prolog-подобным стилем работы со списками, где приходилось откусывать по одному элементу списка с начала или конца.

Во многих шаблонных библиотеках (в том числе в стандартной библиотеке C++) реализовывали вспомогательные шаблонные механизмы для работы с variadic templates:

template <std::size_t Index, class P0, class... Pack>
struct nth {
    using type = typename nth<Index - 1, Pack...>::type;
};

template <class P0, class... Pack>
struct nth<0, P0, Pack...> {
    using type = P0;
};

template <std::size_t Index, class... Pack>
using nth_t = typename nth<Index, Pack...>::type;

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

В комитете давно бытует мнение, что метапрограммирование должно быть похоже на обычное программирование. И если у нас есть последовательность, то очевидно должен быть способ обратиться к элементу по его индексу. Благодаря предложению P2662R3, прошлый развесистый код из примера в C++26 можно просто заменить на Pack...[I].

Работает индексирование и для списка переменных:

[](auto... args) {
    assert(args...[0] != 42);
    // ...
}

Кстати, об assert

Вменяемый assert


Наверняка вы когда-то писали что-то похожее на это: assert(foo<1, 2>() == 3). И после этого получали затейливое сообщение об ошибке: error: macro "assert" passed 2 arguments, but takes just 1. Код можно поправить, если добавить дополнительные круглые скобки, от чего он красивее не становился.

С предложениями N2829 и P2264R7 в C23 и C++26 assert-макросы работают без лишних скобочек и телодвижений прямо из коробки.

Арифметика насыщения


Заголовочный файл <numeric> оброс дополнительными методами для работы с арифметикой насыщения:

  template<class T>
    constexpr T add_sat(T x, T y) noexcept;           // freestanding
  template<class T>
    constexpr T sub_sat(T x, T y) noexcept;           // freestanding
  template<class T>
    constexpr T mul_sat(T x, T y) noexcept;           // freestanding
  template<class T>
    constexpr T div_sat(T x, T y) noexcept;           // freestanding
  template<class T, class U>
    constexpr T saturate_cast(U x) noexcept;          // freestanding

Эти методы при переполнениях операции возвращают максимальное/минимальное число, которое может содержать определённый тип данных. Проще всего понять на примере с unsigned short:

  static_assert(std::numeric_limits<unsigned short>::max() == 65535);

  assert(std::add_sat<unsigned short>(65535, 10) == 65535);
  assert(std::sub_sat<unsigned short>(5, 10) == 0);
  assert(std::saturate_cast<unsigned short>(100000) == 65535);
  assert(std::saturate_cast<unsigned short>(-1) == 0);

Все подробности доступны в предложении P0543R3.

Линейная алгебра


Свершилось! В C++26 добавили функции для работы с векторами и матрицами. Более того — новые функции работают с ExeсutionPolicy, так что можно заниматься многопоточными вычислениями функций линейной алгебры. Вся эта радость работает с std::mdspan и std::submdspan:

#include <linalg>

constexpr std::size_t N = 40;
constexpr std::size_t M = 20;

std::vector<double> A_vec(N*M);
std::vector<double> x_vec(M);
std::array<double, N> y_vec(N);

std::mdspan A(A_vec.data(), N, M);
std::mdspan x(x_vec.data(), M);
std::mdspan y(y_vec.data(), N);

// Заполняем значениями A, x, y.
// <...>

// y = 0.5 * y + 2 * A * x
std::linalg::matrix_vector_product(std::execution::par_unseq,
  std::linalg::scaled(2.0, A), x,
  std::linalg::scaled(0.5, y), y
);

Авторы предложения P1673 по линейной алгебре приводят таблицы как BLAS и LAPACK имена функций мапятся на C++26 имена функций из std::linalg::. В стандарте BLAS/LAPACK имена тоже доступны в виде заметок.

Также в черновик стандарта включили оптимизацию взаимодействия std::submdspan с BLAS-имплементациями в P2642.

Атомарные fetch_max и fetch_min


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

То есть в P0493 добавились атомарные операции atomic_variable_or_view = std::max(atomic_variable_or_view, x) и atomic_variable_or_view = std::max(atomic_variable_or_view, x) для std::atomic и std::atomic_view.

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

Приятные мелочи


std::span обзавёлся методом at(std::size_t) и инициализацией от std::initializer_list (P2821 и P2447).

Добавлен метод std::runtime_format(str) (P2918) для подставления в std::format рантайм строк формата (человекочитаемая замена для std::vformat).

В P2542 добавили std::views::concat для последовательной выдачи элементов из нескольких контейнеров:

std::vector<int> v{0, 1};
std::array a{2, 3, 4, 5};
auto s = std::views::single(6);
std::print("{}", std::views::concat(v, a, s));  //  [0, 1, 2, 3, 4, 5, 6]

Добавили конкатенацию std::string и std::string_view через оператор +. Теперь std::string{"hello"} + std::string_view{" world!"} скомпилируется (P2591).

Благодаря P0609, на элементы structured bindings теперь можно навешивать атрибуты: например, auto [a, b [[maybe_unused]], c] = f().

Алгоритмы, ranges и некоторые функции обзавелись возможностью работать с std::initializer_list напрямую в P2248. Например:

struct Point { int x; int y; };

void do_something(std::vector<Point>& v) {
    std::erase(v, {3, 4});
    if (std::ranges::contains(v, {4, 2}) {
        std::fill(v.begin(), v.begin() + v.size() / 2, {42, 0});
    }
}

Если вам необходимо генерировать много случайных чисел, то P1068 добавляет замечательные функции std::generate_random. Они позволяют эффективно создавать множество чисел в ~10 раз эффективнее, чем при простом многократном вызове генератора.

Планы и прогресс по большим задачам


В комитете активно идёт работа над статической рефлексией. Больших проблем и возражений по ней нет.

Тем временем контракты опять вызвали бурные обсуждения. Предстоит подумать над тем, как уменьшить их влияние на размер итогового бинарного файла. Также предстоит сделать прототип решения и отладить его на функциях из стандартной библиотеки. Работы очень много: есть опасения, что контракты могут не успеть к C++26.

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

Очень приятная возможность языка вот-вот подъедет в предложении P1061. С помощью неё можно раскладывать кортежи и агрегаты на элементы, не зная количество этих элементов:

template <class Function, class T>
decltype(auto) apply(Function&& f, T&& argument) {
    auto& [...elements] = argument;
    return std::forward<Function>(f)(std::forward_like<T>(elements)...);
}

template <class Target>
auto make_from_tuple(Tuple&& tuple_like) {
    auto& [...elements] = tuple_like;
    return Target(std::forward_like<T>(elements)...);
}

Вместе с индексированием мы получаем необычайно мощный инструмент для обобщённого программирования:

void my_function(auto aggregate) {
    auto [... elements] = aggregate;
    foo(elements...[0], elements...[1]);
    bar(elements...[2], elements...[3]); 
}

Эта функциональность покрывает большинство возможностей Boost.PFR на уровне языка. Нам в Рабочей Группе 21 предстоит хорошенько подумать над предложением P2141R1, что же из Boost.PFR имеет смысл дотащить до стандарта.

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


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

Пользуясь случаем, хочу пригласить читателей на несколько конференций:

  • В Санкт-Петербурге состоится Встреча РГ21 С++, где мы расскажем подробности о новинках C++ и ответим на ваши вопросы. Не забудьте зарегистрироваться.
  • Летом состоится конференция C++ Zero Cost Conf. Если планируете выступать, то уже можно подавать доклады.
  • А уже в мае пройдёт конференция C++ Russia, где будет множество интересных докладов, в том числе и от нас.
  • Начался набор в Школу бэкенд-разработки. Если вы хотели научиться написанию кода для высоконагруженных веб‑сервисов — тут вам помогут.

Комментарии (103)


  1. boldape
    28.03.2024 07:42
    +3

    А можете подробнее рассказать про индетерминэйт? Из примера создаётся впечатление что этот атрибут стоит не там. В примере он описывает переменную на стэке и вы утверждаете что это говорит компилятору что все ок. Т.е. если функция филл вместо записи сначало прочитает, то все как раньше УБ.

    С другой стороны если этот атрибут применить к параметру функции тогда это ИМХО как раз могло бы помочь компилятору диагностировать чтение из неинициализированной переменной.

    Вы точно не перепутали ничего в примере?


    1. antoshkka Автор
      28.03.2024 07:42

      В примере всё верно. Атрибут можно применять к переменным (как в примере в посте), так и к параметрам функций:

      The attribute-token indeterminate may be applied to the definition of a block variable with automatic storage duration or to a parameter-declaration of a function declaration.


      1. unreal_undead2
        28.03.2024 07:42
        +1

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


        1. antoshkka Автор
          28.03.2024 07:42
          +1

          будет warning


      1. rotor
        28.03.2024 07:42
        +6

        Тоже зацепился глаз за это предложение.
        Только функция знает допускает ли её параметр неинициализированную переменную. И это часть сигнатуры функции.
        Давая возможность помечать переменные [[indeterminate]] мы потенциально делаем код неустойчивым к изменениям.
        Если мы в какой то момент заменим void fill(int&) на void bar(int&), которая не предполагает использование неинициализированных переменных, то мы не получим диагностических сообщений.


        1. unreal_undead2
          28.03.2024 07:42
          +1

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


      1. boldape
        28.03.2024 07:42
        +1

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

        ИМХО использовать этот атрибут на стэке надо сразу запретить во всех гайдлайнах как опасный. Честно говоря я вообще не понимаю зачем этот атрибут для стэка?

        Ещё раз перечитал текст, я начинаю подозревать, что без объявления переменной на стэке с атрибутом, НО пометкой параметра функции филл этим атрибутом компилятор ПРОДОЛЖИТ считать тело сэмпл эрониус.

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


        1. KanuTaH
          28.03.2024 07:42
          +1

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

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

          Честно говоря я вообще не понимаю зачем этот атрибут для стэка?

          Затем, что без этого атрибута будет erroneous behavior, поскольку компилятор не уверен, что функция запишет в переменную до того, как прочтёт из неё (напомню, что реализация функции компилятору может быть недоступна, см. выше). Но если вы уверены, что это действительно так, то ставите атрибут и erroneous behavior отключается. Это opt-out механизм. Прочтите P2795 по ссылке в статье, там все подробно расписано.

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

          Предложите "более лучший" пропозал. Это открытый процесс.


          1. boldape
            28.03.2024 07:42

            В общем случае это невозможно проверить

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

            Компилятору будет доступна только декларация прототипа

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

            Затем, что без этого атрибута будет erroneous behavior, поскольку
            компилятор не уверен, что функция запишет в переменную до того, как
            прочтёт из неё

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

            Предложите "более лучший" пропозал

            Я просто скопирую то что УЖЕ работает в других языках.

            Как сейчас в плюсах - на КАЖДОЕ использование неинит переменной компилятор/стат анализ кидает ворнинг. Нельзя одним махом взять и подавить все ворнинги ТОЛЬКО к этой переменной (можно в блоке, файле, программе). Т.е. инструменты требует от вас просмотреть каждый колл сайт и для каждого индивидуально решить фиксить или давить ворнинг.

            Как теперь будет (проползал то уже приняли). Вы в ОДНОМ месте ставите атрибут и компилятор затыкается во ВСЕХ кол сайтах. Я не могу усилить форматированием слово во всех ещё сильнее, даже в тех кол сайтах которые ещё только БУДУТ написаны. Это делает проблему использования неинит переменных ещё опасней чем она есть сегодня.

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

            void sample(){
              int hz;
              foo([[uninitialised]] &hz);
              bar(hz);
            }
            
            void foo([[uninitialised]] int& hz){
              auto garbage=hz;//error
              hz=0;
              auto var=hz;
            }
            
            void bar(const int& val){
              auto var=val;
            }

            Если удалить атрибут в месте вызова фуу то будет 2 ошибки компиляции. В моем примере при этом для бара атрибут НЕ нужен ЕСЛИ у фуу в декларации тоже есть этот атрибут. Почему? Потому что функция в кол сайте которой есть этот атрибут гарантирует инициализацию аут параметра. Т.е. после вызова фуу хз точно инициализирована в том же самом скоупе (что это значит чуть позже).

            Дальше функция фуу находится в чужой либе и вы не можете менять ее (ни декларацию ни определение) - да НЕ проблема ставите как и в примере атрибут в кол сайте, все тоже самое бар уже НЕ требует атрибута. Пришел Вася Пупкин через 5 лет поменял местами фуу и бар и/или вставил фуубар перед фуу и получил ошибку компиляции. И теперь сидит и курит почему так.

            Внутри функции чей параметр помечен атрибутом при доступе к параметру ДО инициализации - ошибка компиляции. Ретурн или сроу ДО инициализации параметра - ошибка компиляции. Вызов не ноуэксепт функции/оператора ДО инициализации - ворнинг.

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

            Я НИЧЕГО нового здесь не написал, а то что попало в стандарт тупо ещё опасней чем как сейчас.


            1. KanuTaH
              28.03.2024 07:42

              А как тогда получилось у других языков?

              Ну насчёт идеи указывать атрибут при передаче аргумента при вызове функции, а не в момент объявления переменной я и сам уже думал, но тут хочу возразить - вопрос-то от вас был тут вот какой:

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

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

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

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

              void fill([[indeterminate]] int &arg);
              
              int x;
              
              fill(x);

              erroneous behavior не возникнет, даже если x объявлена без атрибута. А на стеке этот атрибут нужно указывать только если он не используется в сигнатуре соответствующего метода или оператора (потому что соответствующий код написан под предыдущий стандарт). Но, честно говоря, этот момент действительно не очень понятен. Подождём разъяснений начальника транспортного цеха.

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


              1. boldape
                28.03.2024 07:42

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

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

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

                Массовый сценарий - при переходе будет туча эрониус бихэвиоров что-то конечно пофиксят, ну а что-то будет помечено этим атрибутом и тогда ЛЮБОЙ код с такой переменной должен приниматься компилятором как есть без права на ворнинг.

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

                Вот кто то спросит на стэке - у меня эрониус бихэвиор, что мне делать - да просто добавь атрибут - спасибо помогло, а мне потом это чинить - спасибо я такое буду у себя на работе запрещать.

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


                1. KanuTaH
                  28.03.2024 07:42
                  +1

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

                  Да нет, я так понимаю (из текста пропозала и из объяснений автора статьи), что будет работать как-то так:

                  void foo([[indeterminate]] int &arg);
                  void bar(int &arg);
                  
                  {
                    int x;
                  
                    if (...) {
                      foo(x); // OK
                    } else {
                      bar(x); // Warning
                    }
                  }
                  
                  {
                    int x;
                    
                    foo(x); // OK
                    bar(x); // OK
                  }
                  
                  {
                    int x;
                    
                    bar(x); // Типа после Васи Пупкина - warning
                    foo(x); // OK
                  }

                  А насчет идеи с переносом [[indeterminate]] из объявления в вызов я в целом согласен, что так было бы лучше:

                  // Не очень вариант
                  {
                    [[indeterminate]] int x;
                    
                    bar(x);
                  }
                  
                  // Вариант получше
                  {
                    int x;
                    
                    bar([[indeterminate]] x);
                  }
                  

                  Но вот с таким предложением я не согласен категорически:

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

                  Это тупо сломает кучу абсолютно рабочего кода наподобие:

                  int x;
                  
                  std::cin >> x;

                  С пропозалом из статьи достаточно будет в декларацию operator>>() добавить для соответствующего аргумента атрибут [[indeterminate]] и васякот, а заставлять расставлять этот атрибут ЕЩЕ И В МЕСТАХ ВЫЗОВА - это бесполезная работа, ничего не дающая абсолютно. В местах вызова он должен расставляться только если речь идет о вызове функций, написанных на старых стандартах без поддержки этого атрибута.


            1. KanuTaH
              28.03.2024 07:42

              P.S.

              Как теперь будет (проползал то уже приняли).

              Приняли пока только в черновик. Думаю, что будет ещё дорабатываться.


        1. Elamine
          28.03.2024 07:42

          Причем там стэк? Там же написано под комментарием что без атрибута будет ошибка! Это происходит во время компиляции. Причем стэк если код даже не скомплировался прокрутил этот вопрос но в разборе не нахожу куда могу его засунуть?! Этот Атрибут как будто говоришь компилятору что у тебя есть неинициалированная переменная но обещаешь что у тебя есть функция которая будет задать ей значение. Это стремление к устранению использования неинициалированных переменных. Даётся ощущение, что все и только ждут всячески мелочей чтобы зацепиться на плюсах. И не путай США противно всё над которым не имеет власть. Это не их первая попытка что касается плюсов.


          1. antoshkka Автор
            28.03.2024 07:42

            На всякий случай подсвечу, что "ошибочное поведение != ошибка компиляции". На ошибочное поведение компилятор выдаст предупреждение.

            Подсвечу это в статье поярче


            1. Elamine
              28.03.2024 07:42
              +1

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


  1. webhamster
    28.03.2024 07:42

    struct Y {

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

    const std::pair<std::string, int>& first() const {
    return *d_map.begin(); // тут возвращается std::pair<CONST std::string, int>
    }

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

    "Тут возвращается std::pair<CONST std::string, int>" - и что? Что должен понять читатель? Возвращается где? В return или в результате вычисления выражения *d_map.begin()? Возвращается что? Ну напишите вы что в first-элементе пары строка константная и объясните почему. Напишите как этот тип будет кастоваться к возвращаемому типу и допустимо ли такое кастование, и как это относится к озвученной проблеме. Или не относится?

    И самое главное - что этот пример демонстрирует в контексте возврата ссылки на временное значение? Объясните хотя бы что begin() - это не первое значение, не указатель на первое значение, а итератор, указывающий на первое значение. Что звездочка - это не разыменовывание указателя, а "разыменовывение" итератора. И объясните наконец, почему ссылка на пару, на первый взгляд находящуюся в вышестоящем скопе, становится вдруг ссылкой на временное значение. Скажите, хотя бы, что пара не хранится в map, и она является временным объектом, хранящем ключ-значение. И именно на этот временный объект и создается ссылка, и поэтому ее передавать при возврате нельзя.

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


    1. iliazeus
      28.03.2024 07:42
      +24

      У любой статьи есть своя целевая аудитория. Статья про новые стандарты C++ ожидает, что читатель знаком с современным C++. Как мне кажется, это вполне логичное ожидание. А у такого человека не возникнет вопросов вроде:

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

      "Тут возвращается std::pair<CONST std::string, int>" - и что? Что должен понять читатель?

      Объясните хотя бы что begin() - это не первое значение, не указатель на первое значение, а итератор, указывающий на первое значение.


      1. ExCyB
        28.03.2024 07:42
        +4

        Перечисленные претензии, действительно, кажутся странными. Однако пояснить, что дело именно в const, из-за которого и возникает временный объект, мне кажется, все же стоит: статьи читают не только суперпрофи в C++, но и новички, школьники и студенты, и им будет сложно разобраться в проблеме (где именно проблема возникает, какая и почему) без подсказки. А из нынешнего текста статьи даже не вполне очевидно, что проблема там вообще есть.


        1. KanuTaH
          28.03.2024 07:42
          +6

          Ну вот представьте, публикует некто в научном журнале статью, посвящённую там, условно, неким направлениям в решении какой-то проблемы СТО/ОТО. Целевая аудитории статьи - люди, которые интересуются вопросом, стало быть, они знакомы с проблематикой, вопросов к матаппарату у них тоже нет, просто кто-то считает какие-то направления бесперспективными, кто-то предлагает что-то свое, и так далее. И тут внезапно в обсуждение врывается школотрон, который с ходу заявляет, что ему мол ничего не понятно, что ещё за тензорное исчисление такое, хамит автору и требует(sic!) все изложить от Адама и Евы на уровне, доступном человеку с тремя классами образования. Абсурд? Да, абсурд. Но пациент ведёт себя именно так.


          1. ExCyB
            28.03.2024 07:42

            Я далек от мысли защищать пациентов. И даже не лезу в дискуссию о стандарте со стариковским брюзжанием, что раньше всё было лучше (хотя очень хочется: современный подход к UB меня бесит, но вроде начали, наконец, хоть где-то что-то исправлять, так что призываю этот текст в скобочках не замечать :) ). Но вот в идее пояснить или даже просто выделить как-то ошибку в приведенном ошибочном примере, раз уж он приведен... Что-то в этой идее есть, согласитесь.

            Кстати, только вот на днях беседовал с начинающим программистом C++, который вообще не понимает, зачем нужен const. И, знаете, это заразно: сижу теперь, думаю... Вдруг, и вправду, можно сформировать семантику языка так, чтобы было из контекста понятно, какие элементы изменяемые, а какие - нет? Причем без изменения привычного синтаксиса? Хотя, все-таки, больше похоже на тяжелый бред.


            1. KanuTaH
              28.03.2024 07:42
              +1

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

              Ну пусть попишет без него. Пару раз поищет, где же у него неожиданно поменялись кишки объекта, который он передал в 100500 мест по T& вместо const T& - глядишь, и поймёт.

              Вдруг, и вправду, можно сформировать семантику языка так, чтобы было из контекста понятно, какие элементы изменяемые, а какие - нет?

              Если под "из контекста" вы подразумеваете "если что-то изменяется, то это не const", то спасибо, но лучше не надо. Константность должна быть частью контракта, потому что это в том числе защита для погромиста от случайных ошибок.


              1. ExCyB
                28.03.2024 07:42

                Если под "из контекста" вы подразумеваете "если что-то изменяется, то это не const"

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

                1. берем базу программ побольше. Сносим все const (и во всех библиотеках тоже, конечно). Учим нейросетку их восстанавливать. Хорошо бы при этом как-то запретить ей смотреть, изменяется что-то, или нет. Обрезать весь код, например. Если ей это хорошо удается - значит, это и есть способ. А где не восстановились - может, там они и не нужны были?

                2. (дальше буду утрировать до полного бреда, зато будет, надеюсь, понятна идея, а в реальности такое надо делать намного аккуратнее) просто договариваемся, что все глобальные переменные отныне const, все static члены - не const, первый и третий аргументы в функции всегда const, а второй и остальные - не const. В паре ключ - const. Ну, и так далее... Синтаксис языка сохраняется. Семантика - перекурочивается. const больше нет, но все знают, как сделать константу. Сделать такое (фактически, новый язык) почти наверняка можно, но можно ли это сделать удобным? Вряд ли, но - а вдруг?


    1. sabudilovskiy
      28.03.2024 07:42
      +9

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

      Тут возвращается std::pair<CONST std::string, int>" - и что? Что должен понять читатель? Возвращается где? В return или в результате вычисления выражения *d_map.begin()? Возвращается что? Ну напишите вы что в first-элементе пары строка константная и объясните почему. Напишите как этот тип будет кастоваться к возвращаемому типу и допустимо ли такое кастование, и как это относится к озвученной проблеме. Или не относится?

      Не относится. Всем и так всё ясно. Плюсовики не приходят в чужие статьи и не ноют, почему не расписали всё для пятилетних детей.

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

      Ваши проблемы.


      1. webhamster
        28.03.2024 07:42

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

        А чего тогда не пишите union? Так короче будет, и тоже все public.

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

        Опять структуру классом назвали, а не наоборот. Да как же так?

        Не относится. Всем и так всё ясно.

        Отучаемся говорить за всех.

        Ваши проблемы.

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

        И еще просьба: пишите ваши посты с соблюдением разметки. Чтобы было видно на какие вопросы вы отвечаете. Уважайте читателей.


        1. ZirakZigil
          28.03.2024 07:42
          +9

          А чего тогда не пишите union

          Потому что struct это для типов-произведений, а union — для типов-сумм.


          1. webhamster
            28.03.2024 07:42

            Давайте так напишем, будет еще короче:

            std::map<std::string, int> d_map;

            union Y {
            const std::pair<std::string, int>& first() const {
            return *d_map.begin(); // тут возвращается std::pair<CONST std::string, int>
            }
            };

            Вот это ляпота! Не ясно зачем, но стало короче. И тип-сумма сама с собой не пересекается. И union Y можно использовать как класс, до поры до времени. Ну красота же!

            Что, не нравится? Не поняли? Не смешно? Это C++! И когда Lisp с плюсами - Java будет трепетать!


            1. alexeibs
              28.03.2024 07:42
              +8

              union в С++ - это удобный инструмент для отстрела конечностей, своих и чужих. В то время как struct - это тот же класс, но с более удобными модификаторами доступа по умолчанию. struct практичен, позволяет писать более короткий и ясный код. Минус у него один - находятся эстеты, которым нравится писать слово "class", а "struct" их коробит по причинам зачастую не вполне рациональным.


              1. webhamster
                28.03.2024 07:42

                Хоть один адекватный человек нашелся.

                Минус у него один - находятся эстеты, которым нравится писать слово "class", а "struct" их коробит по причинам зачастую не вполне рациональным.

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


                1. ZirakZigil
                  28.03.2024 07:42
                  +3

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


                  1. webhamster
                    28.03.2024 07:42

                    Не притрагиваясь к OОП - значит не используя классы, значит самого вопроса о ключевом слове class не возникает.

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


                    1. ZirakZigil
                      28.03.2024 07:42
                      +1

                      И найдут кучу причин почему это удобнее и правильно.

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


                      1. webhamster
                        28.03.2024 07:42

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


                      1. ZirakZigil
                        28.03.2024 07:42
                        +1

                        Это для вас X — красное, а Y — синее, и попытка использовать X вмемсто Y вызывает диссонанс. А для другого существует Z — зелёное — которое он использует там, где вы используете X или Y, и для него вашей проблемы выбора не существует. Так понятно?


                      1. webhamster
                        28.03.2024 07:42

                        Это для вас X — красное, а Y — синее, и попытка использовать X вмемсто Y вызывает диссонанс. А для другого существует Z — зелёное

                        В том то и проблема, что зеленого нет. У нас всего две сущности - class и struct. И этот другой выглядит очень странно, если вместо X начинает использовать Y.


    1. antoshkka Автор
      28.03.2024 07:42
      +18

      Пара хранится в map, но она хранится с константным first. Поэтому *d_map.begin() вернёт `const std::pair<const std::string, int>&`, от которого создастся временный объект `std::pair<std::string, int>`, и ссылка именно на него и вернётся из функции.

      Лично я люблю небольшие C++ загадки, и пишу из ожидания что и читателю они по душе


      1. webhamster
        28.03.2024 07:42

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


        1. antoshkka Автор
          28.03.2024 07:42
          +7

          Языков без загадок не существует

          В некоторых странах "лоббирование" является легальным промыслом. И не смотря на все лоббирования на уровне одного гос. учреждения X в шататх, другое гос. учреждение Y в тех же штатах запрещает использовать рекомендуемые X "надёжные" языки в любых жизненно важных проектах.


          1. webhamster
            28.03.2024 07:42

            Языков без загадок не существует

            К этому надо стремиться. А не воспринимать язык как генератор загадок, которые так интересно решать.


    1. KanuTaH
      28.03.2024 07:42
      +11

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

      Так не читайте :) Не мучайте себя и не мусорите своими комментариями профессионального незнайки. Вы просто не являетесь целевой аудиторией данной статьи.


      1. webhamster
        28.03.2024 07:42

        А какие ошибки были в моем посте, что вы перешли на личности и назвали меня профессиональным незнайкой? Похоже, по степени токсичности хабар начал обгонять LOR и OpenNet вместе взятые?


        1. Chaos_Optima
          28.03.2024 07:42
          +16

          На текущий момент токсичность показываете только вы.


          1. webhamster
            28.03.2024 07:42

            Я кого-то лично задел? Что-то не нахожу, может быть вы найдете.

            А KanuTaH почему-то посчитал, что личное оскорбление - это нормально. И с первой же мессаги начал свои потуги. Даже на ЛОРе с OpenNet начинают хамить со второго-третьего сообщения, а тут рекорды ставят, о чем я и сообщил.

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


            1. Chaos_Optima
              28.03.2024 07:42
              +12

              Лично может не задели, но ваше первое же предложение

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

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


              1. webhamster
                28.03.2024 07:42

                Является пассивно агрессивным, как в прочем и всё остальное что вы писали.

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

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

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


                1. Chaos_Optima
                  28.03.2024 07:42
                  +1

                  И что с того?

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

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

                  Как и вы. Тоже любите говорить за других? Я лишь говорю со стороны своего опыта С++ программиста. В моём понимании человек который не может понять в чём там проблема, с трудом смог бы претендовать на джуновскую позицию, по крайней мере я бы такого на собесе завернул. Так что да, для меня большинство С++ программистов не должны иметь проблем с пониманием того что там происходит.


                  1. webhamster
                    28.03.2024 07:42

                    Думаю, с таким подходом, вы год от года будете видеть все меньше и меньше джунов. И когда-нибудь наступит момент, когда джунов на вашу долю вообще не достанется.

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


                    1. Chaos_Optima
                      28.03.2024 07:42
                      +2

                      Думаю, с таким подходом, вы год от года будете видеть все меньше и меньше джунов.

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

                      В конце концов, если вы наемный работник, то вам на проблемы отрасли можно и наплевать.

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


  1. khizmax
    28.03.2024 07:42

    del


  1. khizmax
    28.03.2024 07:42
    +3

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

    Разберем по порядку.
    Что подводит нас к новому атрибуту [[indeterminate]]. Если у нас есть неинициализированная переменная и есть функция, которая только пишет в переменную, то можно компилятору дать подсказку, что это не ошибочное поведение. Тогда значение из переменной не будут читать в функции

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

    мы в международной группе пришли к такой мысли: «А было бы неплохо выдавать произвольную диагностику и для =delete»

    ИМХО, полезно,но является имитацией бурной деятельности комитета.
    То же самое можно написать в комментарии к строке, где может возникнуть ошибка, только еще более развернуто.

    Эти методы при переполнениях операции возвращают
    максимальное/минимальное число, которое может содержать определённый тип
    данных. Проще всего понять на примере с unsigned short

    Из этих примеров мне понятно одно: при таком вычитании значение 0 является невалидным для unsigned short. Кажется, в комитете сидят умные люди и должны понимать, что средствами диапазона [a, b] нельзя выразить признак, что значение X не входит в этот диапазон. То есть налицо новоприобретенный костыль, который будут выпиливать в C++29.

    Свершилось! В C++26 добавили функции для работы с векторами и матрицами.
    Более того — новые функции работают с ExeсutionPolicy, так что можно
    заниматься многопоточными вычислениями функций линейной алгебры. Вся эта
    радость работает с std::mdspan и std::submdspan

    Я вижу эту "радость" по-другому: вместо того, чтобы описывать матрицу, тензор и пр., мы описываем сначала как эта матрица хранится в памяти, а затем поверх этого нахлобучиваем view, как на это нужно смотреть. Странный способ... То есть мы намеренно выносим кишки (memory representation) наружу, хотя до этого комитет всячески убеждал нас не делать такого. ИМХО, эпический костыль...


    1. KanuTaH
      28.03.2024 07:42
      +8

      Из этих примеров мне понятно одно: при таком вычитании значение 0 является невалидным для unsigned short.

      Почему вы так решили?

      Кажется, в комитете сидят умные люди и должны понимать, что средствами диапазона [a, b] нельзя выразить признак, что значение X не входит в этот диапазон.

      Это вообще никак не связано с "выражением признака, что значение X не входит в диапазон". Просто для некоторых применений (например, цифровая обработка сигналов) быстрая арифметика с насыщением выгодна. Видимо, это сделано с прицелом на то, что компилятор будет лучше оптимизировать такую арифметику с использованием специализированных наборов процессорных инструкций там, где это поддерживается (DSP extensions на ARM, инструкций наподобие _mm_adds_XXX из SSE, и т.д).


      1. khizmax
        28.03.2024 07:42
        +1

        Почему вы так решили?

        Из примера, очевидно:

         assert(std::sub_sat<unsigned short>(5, 10) == 0);
        

        Точно так же - значение 65535 в такой интерпретации не является валидным для ushort_16.

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


        1. KanuTaH
          28.03.2024 07:42
          +8

          Из примера, очевидно:

          Все правильно в этом примере. Если в режиме насыщения из 5 вычесть 10, то получится 0, wrap around, как при модульной арифметике, не произойдет. Все еще непонятно, почему вы решили, что 0 тут - "невалидное значение".


          1. khizmax
            28.03.2024 07:42

            assert Вам ничего не говорит?..


            1. KanuTaH
              28.03.2024 07:42
              +5

              А вы, простите, точно понимаете, как assert() работает? А то создается такое впечатление, что нет.


            1. unreal_undead2
              28.03.2024 07:42
              +4

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


            1. feelamee
              28.03.2024 07:42

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

              Если его нет, то все работает так как вы и сказали
              .
              А если есть, то так, как и задумывал автор.


        1. antoshkka Автор
          28.03.2024 07:42

          Точно так же - значение 65535 в такой интерпретации не является валидным для ushort_16.

          В случае арифметики насыщения, краевые значения (0 и 65535) не надо воспринимать как не валидные. Это просто значения

          Пример задачи где такая арифметика хорошо подходит: наполнение резервуара. Есть резервуар на 65535 единиц. Чтобы вылить из него `x`, надо позвать res = std::sub_sat<unsigned short>(res, x), чтобы долить `y` надо позвать res= std::add_sat<unsigned short>(res, y)

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


    1. khizmax
      28.03.2024 07:42
      +1

      я продолжу...

      Если вы часто пользуетесь variadic templates, то вы, скорее всего, настрадались с Prolog-подобным стилем работы со списками, где приходилось откусывать по одному элементу списка с начала или конца.

      Видимо, кто-то из членов комитета спустя 15 лет после C++11, где variadic template появились, наконец-то воспользовался ими в полной мере и понял, что это не так удобно, как казалось. И снизошло озарение - индексы...

      Вместе с индексированием мы получаем необычайно мощный инструмент для обобщённого программирования:

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

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


      1. antoshkka Автор
        28.03.2024 07:42

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

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


      1. antoshkka Автор
        28.03.2024 07:42

        В хорошем примере этот код был бы обложен ограничениями на типы аргументов функций foo и bar.

        Всё верно. В стандарте такого кода не будет, а в статье он без концептов для упрощения примера


  1. frenzis
    28.03.2024 07:42
    +3

    Хоть что-то улучшили по следам этого доклада ?

    CppCon 2018: Nicolai Josuttis “The Nightmare of Initialization in C++” https://www.youtube.com/watch?v=7DTlWPgX6zs


    1. antoshkka Автор
      28.03.2024 07:42
      +1

      `auto i = {42};` уже превратили в ошибку компиляции, разрешили инициализацию агрегатов через круглые скобки, инициализацию атомиков тоже поправили, assert вот тоже поправили (об этом есть в посте).

      Многие другие ужасы уже давно были поправлены, как и говорится в выступлении.

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


  1. Hlad
    28.03.2024 07:42
    +1

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


    1. iliazeus
      28.03.2024 07:42
      +2

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

      Поэтому да, я думаю, что большинство реализаций стандартной библиотеки C++ на поддерживаемых архитектурах будут использовать соответствующие команды процессора. Как это сейчас есть для какого-нибудь std::popcount, например.


    1. iliazeus
      28.03.2024 07:42
      +1

      Кстати, если под проверками вы понимаете именно ветвления вроде if или тернарных операторов, то branchless-реализация возможна даже без применения специальных команд процессора. В тексте пропозала (на него есть ссылка в статье) есть ссылка на https://locklessinc.com/articles/sat_arithmetic/, где как раз описана такая реализация.


    1. antoshkka Автор
      28.03.2024 07:42
      +1

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

      Получается у компиляторов по разному https://godbolt.org/z/Kfc1KhqKT

      Заведу бегрепорты на компиляторы, где можно улучшить


  1. User239_30b
    28.03.2024 07:42
    +2

    Хочется спросить, линейная алгебра предполагается быть для того чтобы было, или она должна быть из коробки на уровне Eigen или Intel mkl?


    1. antoshkka Автор
      28.03.2024 07:42

      Она должна быть на очень хорошем уровне, на уровне BLAS/LAPACK. Авторы предложения заморачивались с тем, чтобы линал был бинарно совместим с эталонными реализациями BLAS/LAPACK. И соответственно, чтобы можно было использовать эталонные реализации напрямую в имплементации стандартных библиотек.


      1. Ingulf
        28.03.2024 07:42

        но при этом наружу торчит std::vector<double> A_vec(N*M); вместо инкапсулирующего внутренности класса?


        1. antoshkka Автор
          28.03.2024 07:42
          +1

          Все операции работают над std::mdspan. В примере std::vector для того, чтобы показать как std::mdspan создать над массивом данных.

          Да, std::mdspan не владеющий класс. Посмотрите примеры в proposal, часто нужны операции над частью матрицы и соответственно на практике нужен именно не владеющий класс


    1. unreal_undead2
      28.03.2024 07:42
      +1

      Разработчики MKL в соавторах пропозала есть, наверное не просто так )


  1. vanxant
    28.03.2024 07:42
    +3

    Добавить в стандарт типы с насыщением и наконец-то матрицы и не добавить к ним перегрузку операторов? Нде. А зачем тогда вообще вводили операторы в язык? Перегружать && и запятую?:)


    1. iliazeus
      28.03.2024 07:42

      В пропозале про операции с насыщением про это говорится так:

      Instead of free functions, it is conceivable to provide an integer-like class template with the arithmetic operators suitably overloaded. This would, however, make it impossible to adopt this proposal for C, and seems slightly over-engineered for a rather simple facility.


      1. vanxant
        28.03.2024 07:42

        Простите, это просто рукалицо.

        Нельзя так сильно ненавидеть своих пользователей.

        Нельзя в 2024 тащить в язык новые математические типы и заставлять юзера вычислять формулы в этих типах пошагово.

        Фортран 70 лет назад взлетел на трансляции формул (собственно, он и называется FORmula TRANslator)

        Да что фортран, в своё время тип auto завезли в плюсы с формулировкой "сложно вывести вручную тип для шаблона operator* умножения матриц"


        1. vanxant
          28.03.2024 07:42

          Продолжу свою вчерашнюю мысль.

          В результате в Си явочным порядком завезут функции типа sat_add_i32, sat_mul_u16 и пр. Потому что, ну, всё равно для плюсов писать, а компиляторы-то одни и те же.

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

          А стандартная библиотека плюсов и в этой области займёт своё привычное место, когда "никто не использует STL" (с)


    1. antoshkka Автор
      28.03.2024 07:42
      +1

      Авторы предложения на линал хотели добавить операторы, но столкнулись с целой кучей проблем https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p1673r13.html#arithmetic-operators-and-associated-expression-templates

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

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


      1. vanxant
        28.03.2024 07:42
        +1

        Спасибо за ссылку, ознакомился.

        Our goal is to propose a low-level interface

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

        PS. Во всех известных мне имплементациях линала умножением называется dot product. Всё остальное имеет другие названия функций и операторов. Чушь про возможность задать другой базовый тип для возвращаемых значений даже не хочу комментировать.


        1. iliazeus
          28.03.2024 07:42
          +1

          Во всех известных мне имплементациях линала умножением называется dot product.

          Знаю как минимум несколько реализаций линала или близких к нему вещей, где произведение через оператор * - это поэлементное произведение.


  1. algol
    28.03.2024 07:42

    template <typename T, typename Func>
    T AtomicUpdate(std::atomic<T>& atomic, Func updater) {
      T old_value = atomic.load();
      while (true) {
        // make a copy to to keep old_value unchanged
        const T new_value = updater(T{old_value});
        if (old_value == new_value) return old_value; // don't mark cache line as dirty
        if (atomic.compare_exchange_weak(old_value, new_value)) return new_value;
      }
    }

    Так не лучше?


    1. antoshkka Автор
      28.03.2024 07:42

      О, и правда! Спасибо, сейчас поправим


  1. Ingulf
    28.03.2024 07:42
    +3

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


    1. ZirakZigil
      28.03.2024 07:42
      +4

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


    1. NeoCode
      28.03.2024 07:42
      +4

      network тоже по сути прикладная (точнее библиотечная) вещь. А вот рефлексия - чисто языковая.


  1. Panzerschrek
    28.03.2024 07:42

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

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


    1. unreal_undead2
      28.03.2024 07:42
      +1

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


    1. vanxant
      28.03.2024 07:42
      +2

      Вообще раньше они, кажется, не нули писали, а 0xCCCC...

      Это намного надёжнее в том смысле, что прога сразу падает или начинает творить лютую дичь


      1. Playa
        28.03.2024 07:42
        +3

        Только в отладке.


      1. qw1
        28.03.2024 07:42
        +1

        Это намного надёжнее с т.з. выявления UB, т.е. обращения к неинициализированной памяти.
        Если же, как предложено выше, внести изменение в стандарт, получится как в C# или в Delphi. Там выделенная память под созданный класс, буфер или массив гарантированно заполнена нулями, и её не придётся заново инициализировать (типа все ссылочные переменные в nullptr, все счётчики в 0, и т.п. - это удобно и практично)


        1. antoshkka Автор
          28.03.2024 07:42

          Увы, это наносит удар по производительности. Например убирание такой инициализации нулями позволяет раза в 3 ускорить некоторые горячие пути в коде (https://github.com/userver-framework/userver/commit/a24d86474cb850510484c0d78fa4924bea16505c)


          1. qw1
            28.03.2024 07:42
            +1

            Да, понятно, что это не C-way, я хотел лишь привести пример, когда инициализация нулями уместнее, чем инициализация CC (и наоборот).
            В языках с GC инициализация нулями практически бесплатная, т.к. память обнуляется в отдельном потоке, массово, а на стеке почти ничего нельзя создать.


            1. antoshkka Автор
              28.03.2024 07:42

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


          1. vanxant
            28.03.2024 07:42

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

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


            1. qw1
              28.03.2024 07:42
              +1

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


              1. vanxant
                28.03.2024 07:42

                ну если у меня class Point3d с полями x, y и z, которые всегда устанавливаются в конструкторе из его параметров, то зануление не нужно. Если у меня class MapMarker с полями Point3d point и std::string text, то аналогично


                1. qw1
                  28.03.2024 07:42

                  std::string в таком подходе должен быть написан так, что пустая строка - обнулённый кусок памяти (size = capacity = dataptr = 0). Тогда получим приличный выигрыш, если выделяем массив строк - память под ними можно разом обнулить, не вызывая конструкторы в цикле. Тогда и полное обнуление памяти под массивом из MapMarker уже не так страшно будет, по сравнению с текущей ситуацией, когда text надо инициализировать, а point - не надо.


  1. Elamine
    28.03.2024 07:42
    +3

    На счёт networking сложная тема для стандартизации так как такая библиотека больше реактивна потребует постоянного обновления и как только какая-то уязвимость найдётся то потребуется немедленного устранения данной уязвимости без всякого консенсуса даже если это означает сломать кому-то базу унаследованного кода. Библиотека networking ждать три года без обновления очень очень сложно представиться мне


    1. ZirakZigil
      28.03.2024 07:42
      +1

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


  1. Playa
    28.03.2024 07:42
    +2

    А что случилось с полпозалом про _? Почему вместо него решили сделать auto [a [[maybe_unused]], b] = foo();?


    1. iliazeus
      28.03.2024 07:42
      +3

      Так вроде ничего не случилось? Пропозал, насколько я понял, приняли еще летом. cppreference даже говорит, что в новых clang и gcc он уже реализован.

      Просто фичу с навешиванием атрибутов на structured bindings трудно продемонстрировать на чем-то другом. Дело в том, что в самом стандарте есть только один атрибут, который имеет смысл так указывать - это как раз [[maybe_unused]]. Но фича нужна, в том числе, для вендорских атрибутов, не только для стандартных.


  1. JordanCpp
    28.03.2024 07:42

    А уже были комменты, что в rust'е всё сделано лучше и безопаснее?:)


  1. exoron
    28.03.2024 07:42

    Я правильно понимаю, что теперь код вроде

    int x;
    std::cin >> x;

    ...будет триггерить warning в отсутствие аттрибута [[intermediate]] ?


    1. antoshkka Автор
      28.03.2024 07:42

      Стандартную библиотеку C++ (и скорее всего C) разметят этими атрибутами и не erroneous поведение (как в случае с std::cin) не будет приводить к предупреждениям.

      В остальных местах могут появиться предупреждения