Pull to refresh

Comments 119

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

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

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

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

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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Затем, что без этого атрибута будет 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 лет поменял местами фуу и бар и/или вставил фуубар перед фуу и получил ошибку компиляции. И теперь сидит и курит почему так.

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

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

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

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

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

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

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

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

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

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

int x;

fill(x);

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

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

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

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

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

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

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

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

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

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

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

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

P.S.

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

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

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

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

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

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

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, и она является временным объектом, хранящем ключ-значение. И именно на этот временный объект и создается ссылка, и поэтому ее передавать при возврате нельзя.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Есть отличный доклад с C++ Russia: какие фичи Rust я хочу увидеть в C++, ну или как-то так называется. Посмотрите, рекомендую ;)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 будет трепетать!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

И что с того?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.

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

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

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

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

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

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

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

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

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

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

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

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

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

Our goal is to propose a low-level interface

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

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

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

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

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;
  }
}

Так не лучше?

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

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

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

ну пока что каждый следующий стандарт доделывает предыдущий, уж имхо можно допилить фичу до конца, затем полноценно запустить, да это может быть не каждый три года), но оно и лучше: язык получает полноценный функционал
не надо учить 10-15 стандартов за карьеру
а для опробования функционала можно добавить реализацию пропозалов в экспериментальную секцию

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

не надо учить 10-15 стандартов за карьеру

Вы так говорите, будто там перелопачивают всё подряд каждый раз. Есть крупные, вроде 11, 20 и, надеюсь, 26, но в том же 17, например, нет ничего такого что нужно было бы прямо как-то изучать.

а для опробования функционала можно добавить реализацию пропозалов в экспериментальную секцию

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

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

согласен, на счет библиотечности, но пока получился сырой линал, где по-прежнему надо думать как расположить матрицу в памяти, якобы во благо, но такое
и линал и network такие же библиотечные как filesystem, и все же хотелось бы обещанные и давно напрашивающиеся вещи увидеть, а пока, получается, очередной сырой стандарт в который не успели то, что обещали, но внесли задел для доработок на c++29

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

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

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

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

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

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

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

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

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

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

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

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

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

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

но всего на пару процентов максимум

ну отлично, датацентр на 100 полок будет требовать 102 полки, ну и дополнительных 100квч электроэнергии каждый месяц пожалуйста!

И это в среднем на пару процентов. А какие-то специфичные приложения просядут процентов на 10, упс, подумаешь дополнительных 20 лямов баксов вложить чтобы поисковые запросы пользователей обслуживать или их видосики пережимать?

Это нормальный процесс в разработке ПО. С одной стороны, команда постоянно оптимизирует продукт, выжимая лишние проценты производительности. С другой стороны, бизнес простоянно наваливает новые требования, и ему пофигу, что увеличив например идентификаторы с 32 бит до UUID или строку поиска с 64 знаков до 1000 он рушит какие-то выстраданные уникальные оптимизации и проваливает производительность на десятки процентов.

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

ну если какая-то компания решила всё занулять - счастья им и здоровья. Тащить это в стандарт не надо!

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

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

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

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

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

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

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

int x;
std::cin >> x;

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

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

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

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

В векторе можно удалить один элемент или диапазон. А можно ли наоборот - оставить только этот элемент/диапазон, удалив всё остальное? Не знаю даже как искать такое в поиске, не выходит найти.

Приходится вручную удалять всё после диапазона и до.

Группа недоступна.

Похоже, такой простой возможности нет и в Питоне.

В итоге других подобных мест нет? stdcpp.ru, как я понял, мёртв после украинских событий.

А то придумал ещё одну фичу, и она может быть оригинальной и крутой.

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

Sign up to leave a comment.