All streams
Search
Write a publication
Pull to refresh
2
0

User

Send message

И всё‑таки на вопрос «У мамы Натали четверо детей: Саша, Маша, Коля и Никита. Сколько у неё дочек?» мне ответы от нейронок так пока никто и не показал.

Ответ ChatGPT.

И вот вопрос на миллион долларов. Знает ли ИИ, что это ИИ?

Это — вообще не вопрос.
Современному ИИ нечем знать, нет у него такого "Органа", которым можно было бы знать.

В №1 забыли написать:

static_assert(std::size(s_status_str) == MAX_DBG_STATUS, "What???");

Такое даже на голом C можно написать, только std::size необходимо заменить на соответствующее.

То есть, механизм-то — есть, но его не применяют.

Когда они правильно расшифруют, что значит буква I в их AGI, тогда у них появится шанс.

В одном классе я добавил ключевое слово final.

Это — не ключевое слово:

#include <cstdlib>
#include <iostream>

typedef struct final final {
	virtual ~final() final {
	}
} final;

int main() {
	std::cout << sizeof final{} << std::endl;
	return EXIT_SUCCESS;
}

Видите, какое хулиганство?

А всё — потому, что final — не ключевое слово.

Вы в вызовах create* выделяете структуры на стеке....

И ― что?

Circle create_circle(Renderer* renderer, float radius) {
    Circle circle;
    circle.base.renderer = renderer;
    circle.radius = radius;
    return circle;
}

Возврат-то все равно идёт по значению.

Другое дело, что в таком коде логично использовать составной литерал:

Circle create_circle(Renderer* renderer, float radius) {
    return (Circle){.base.renderer = renderer, .radius = radius};
}

Статья полностью дискредитирует ваши курсы!

Статья дискредитирует немного по другой причине.

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

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

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

Ну, и фразы, вроде того, что в C имеются структуры с функциями, конечно, дискредитируют.

--- Не мели ерунды! Вас Людка (жена) каким-то способом вызвала.

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

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

Верно.

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

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

Для данного примера:

using fun = void();

using x = decltype(void());
using y = decltype(fun);
using z = decltype(void (*)());

генерируются типичные сообщения об ошибках:

<source>:4:20: error: unexpected type name 'fun': expected expression
    4 | using y = decltype(fun);
      |                    ^
<source>:5:27: error: expected expression
    5 | using z = decltype(void (*)());
      |                           ^
2 errors generated.

или:

<source>(4): error C3553: decltype expects an expression not a type
<source>(5): error C2760: syntax error: ')' was unexpected here; expected 'expression'
<source>(5): error C2059: syntax error: ')'
<source>(5): error C2059: syntax error: ')'
<source>(5): error C2059: syntax error: ')'

Для 3-ей строки никакой ошибки не сгенерировано.

А в сгенерированных сообщениях об ошибках прямо и недвусмысленно указано: "expected expression" и даже ещё более доходчиво: "decltype expects an expression not a type".

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

Точнее, реальное применение.
Но здесь это — необязательно.

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

Да, верно.

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

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

Да, не понимаю. Вы «исправили» мой код, заменив {} на (), а потом приводите ссылку на Стандарт, говорящий, что «{} или () приводит к такому-то результату» (подразумевая, что разницы нет). И я вот не понимаю: вас здесь ничего не смущает?

Меня — нет, не смущает.

По Стандарту разницы — нет, но clang — глючит, и поэтому при его использовании разница появляется.

Я вам это уже не первый раз объясняю, а вы всё понять не можете.

Именно, не одно и то же (в данном контексте).

Не часто удаётся встретить человека, спорящего со Стандартом.

Правда, ещё непонятно, зачем вы «исправили» мой код на (),

Чтобы clang перестал глючить и заработал согласно Стандарта.

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

При том, что это именно вы утверждали про меня:

Пока что вы показываете полное незнание языка

однако, на поверку, не я, а вы это показываете.

А теперь ещё и спорите со Стандартом.

И, кстати, что же вы тут имели в виду под инстанцированием?

Очевидно, создание объектов.

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

На данный момент сломан clang, а не код, но вам, похоже, — все равно, ведь вам ваши хотелки важнее.

Мы ведь оба понимаем, что это не так? Шаблонную функцию, написанную заранее и давно, которая принимает тип T с полями foo и bar и ожидает, что у них разные адреса, и которая сломается от типа с такой аннотацией, вы можете себе представить?

У меня написано:

Если кто-то сознательно хочет нарушить уникальность адресов ради экономии памяти, зная, что в этом месте ему эта уникальность не нужна

Вы не заметили мою фразу "зная, что в этом месте ему эта уникальность не нужна", верно?

Если тип T с полями foo и bar передаётся в шаблонную функцию, которая ожидает, что у них разные адреса, то это ведь — не тот случай, для которого верно условие "зная, что в этом месте ему эта уникальность не нужна"?

И — не важно, прямо эта уникальность ожидается или косвенно, посредством давно написанной шаблонной функции.

А void() — нет?

Я вам ссылку на Стандарт привёл и даже процитировал.
Похоже, вы на самом деле до сих пор не понимаете, в чём дело.

И статистика по тому, какие круги широкие, а какие — узкие, у вас… откуда?

Можете считать, как хотите.

Я всего лишь задаю вам наводящие вопросы.

Наверное, уже нет необходимости.

Прощу прощенья за то, что у меня пропущена частица «не», что, впрочем, должно было быть очевидно из противопоставления «если вы считаете, что void() что-то там инициализирует, а void{} — нет».

Никакого противоречия не вижу.

Хорошо, первая часть фразы:

Пока что вы показываете полное незнание языка, если для вас void() и void{} одно и то же

Не одно и то же?
Или опять частица «не» оказалась пропущенной?

А что до второго пункта — как считаете, какая часть практикующих программистов (ну, чтобы круги широкие были) знает про эту особенность?

Ну, вот, начались оправдывания...

Так что, есть у меня знания языка или нет?
Или я демонстрирую полное его незнание, как вы пишете?

А может, всё-таки здесь кто-то другой чего-то не знает?

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

Нет, вы считали, что clang работает правильно, а gcc — нет:

Забавно, что gcc вполне себе жрёт typename = decltype(void{}) , и то, что всех это на практике устраивает, как бы намекает нам, что всем пофиг.

Однако, — как раз, наоборот, gcc работает правильно, а clang — неправильно.

Какое-то время назад неправильно работал и gcc, и MSVC.

Во всех основных компиляторах, кроме clang'а, это исправили, думаю, в скором времени, и в clang'е исправят.

И ещё, вы писали:

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

Пример имелся ввиду такой:

боюсь, что уже имеется очень много шаблонного кода, который опирается на то, что void — incomplete-тип (не инстанцируемый), и этот код сломается

Это — по поводу того, как он задумывался.

Что значит «невалидный тип»?

Я же вам привёл пример с void &.

Моя вторая доработка вашего примера работает как раз на основе него.

В рамках этой аналогии — да, есть.

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

Посмотрите значение слова "запретить" в толковом словаре, например, здесь.

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

Это — не моя трактовка, просто таково значение этого слова.

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

Разве я один пишу весь код в мире?

Напишут и вас не спросят.
Именно поэтому вопросы "зачем" в это контексте не имеют смысла.

Расскажите это принявшим [[no_unique_address]], а то они не знают, наверное.

Знают, но по краю ходят.

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

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

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

А, то есть, вы себе это не представляете, но про большинство сказали? Лол.

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

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

Я в курсе, что был Matt Calabrese, которому я лично написал емейл с вопросом о его пропозале по regular void, и который сказал, что у него руки не доходят приехать на заседание Комитета представить свой пропозал. Если на заседании Комитета пропозал никто не представляет, то его не рассматривают изначально, и о «не прошли» (и о мнении большинства) тут говорить вообще нельзя.

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

Да. void{} что делает, по-вашему?

Создаёт prvalue.

Но я и в этом не вижу глубокой проблемы, потому что по факту подобные изменения никого не останавливали: в C++20 завезли (P0960) инициализирование агрегатов из скобочек, и те шаблоны, которые раньше опирались на не-well-formedness выражений T(a, b, c) теперь будут работать неправильно для некоторых T. И ничего, людей это не смущает.

Да, нехорошо получилось.

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

В случае же с void круг случаев значительно шире.

Нет, не кажется. Что такое void() и чем это отличается от void{}?

Значит, самостоятельно вы не смогли разобраться?

Пока что вы показываете полное незнание языка, если для вас void() и void{} одно и то же, и если вы считаете, что void() что-то там инициализирует, а void{} — нет.

Во втором пункте этой части Стандарта написано:

Otherwise, if the type is cv void and the initializer is () or {} (after pack expansion, if any), the expression is a prvalue of the specified type that performs no initialization.

Так одно ли и то же void() и void{} теперь, после приведения мной цитаты из Стандарта?

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

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

На godbolt'е же это легко проверить, "пощёлкав" версиями компиляторов.

У меня есть проверка возможности инстанциирования.

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

Вы определяете тип prvalue expression, а не создаёте временный объект.

И если prvalue expression — валидно, то разве у него может быть невалидый тип?

В одном из своих вариантов я доработал ваш пример так, чтобы невалидный тип (void &) возникал, когда требуется.

И ваш пример стал работать так, как вы задумывали.

На всех компиляторах.

Так в чём суть? Что делает void{}?

Надеюсь, теперь вам это понятно.

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

Если где-то и написан осмысленный код на std::is_constructible<void> то только из-за того, что void-случаи требуют отдельной реализации (как в данной статье). Сам код вероятно не сломается, потому что пойдёт по ветке T - полноценный тип, в том числе и для void. В этом и цель предложения - убрать избыточность/копипасту.

А если void использовался как placeholder для incomplete типа, и взят был именно void, чтобы он случайно не мог быть доопределён и поэтому не мог бы стать complete-типом?

Решения, базирующиеся на этом, сломаются, если void сделать complete-типом.

Очень много. Нигде нет умножения на sizeof(char) в конструкциях типа

То есть, очень мало, а не очень много: практически никто не считает, что это может измениться.

Вот и с void'ом — так же: никто не считает, что void может стать complete-типом.

Вы понимаете практическую пользу от кода, который был вами приведён как пример "поломки"?

Не весь код в мире пишу я: есть ещё очень много других людей, которые тоже пишут код.

И понимать они практическую пользу могут очень по-своему.

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

sizeof(char) по определению равно 1, в отличие от.

Собеседник предложил код:

return std::is_constructible<void>();

и сказал:

"раньше возвращал 0, а будет возвращать 1".

Что тогда мешает в таком же стиле изменить sizeof(char)?

Примерно одного порядка вещи.

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

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

Нет, я имел ввиду ровно то, что написал.
CHAR_BITS существует ещё с древних времён C.

Вам же всю дорогу говорят, что нет у вас никаких «разных» объектов — void, буде он таковым, — будет один.

Мало ли, что говорят.

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

Я же уже приводил вам пример, как так может быть.

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

В первом массиве объекты void неразличимы по адресам, что видно по распечатке их одинаковых адресов, и можно считать, что объект — один.

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

Логично делать UB

Да, логично, и некоторые компиляторы, поддерживающие соответствующее расширение, даже пишут об этом в предупреждении:

<source>:12:22: warning: subtraction of pointers to type 'void0' of zero size has undefined behavior [-Wpointer-arith] 12 | ptrdiff_t delta = p2-p1; // UB?

для этого кода:

#include <cstdlib>
#include <iostream>

typedef struct {
	std::byte a[0];
} void0;

int main() {
	void0 A[7];
	void0* p1 = &(A[1]);
	void0* p2 = &(A[5]);
	ptrdiff_t delta = p2-p1; // UB?

	std::cout << "delta: " << delta << std::endl;
	return EXIT_SUCCESS;
}

Вы обнаружили ещё один прикол, который возникает в адресной арифметике, если сделать размер типа равным 0.

Ещё один аргумент против размера типа, равного 0.

А если разность запрещена, то и обратная операция - тоже.

А вот это — неверно.

Вы же сами приводили пример с делением: делить на 0 нельзя, но умножать-то на него, то есть, выполнять обратную операцию, — можно.

Так и здесь.

Тем, что он взят не из реального проекта.

Там будет эксплуатироваться то же самое.

Суть не видна.

Ну, если суть не видна, то ничем помочь не могу.

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

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

Сколько программ рассчитано на то, что sizeof(char) может быть не равно 1?

Здесь, примерно, — то же самое.

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

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

Вы когда-нибудь задумывались о том, что такое строковый литерал?
Вы уверены в том, что строковый литерал — это объект?

Если обратиться к первоисточнику, то можно узнать, что:

literal is a primary expression.

Никакой не объект, а — выражение.

И далее, первоисточник, 14-ый пункт (в самом низу):

Evaluating a string-literal results in a string literal object with static storage duration, initialized from the given characters as specified above.

Видите, строковый литерал вычисляется (Evaluating a string-literal), потому что это — выражение, а не объект.

А вот вычисление этого выражения уже даёт объект (results in a string literal object).

И — далее, там же:

Whether all string-literals are distinct (that is, are stored in nonoverlapping objects) and whether successive evaluations of a string-literal yield the same or a different object is unspecified.

Прямейшим текстом ещё раз написано, что строковые литералы именно вычисляются (successive evaluations of a string-literal), и что не специфицировано, дают ли последовательные вычисления строкового литерала один и тот же объект или нет (yield the same or a different object is unspecified).

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

И сравнение их через сравнение указателей может работать а может нет.

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

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

Верно, факты я вам привёл.

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

И невозможность их соблюсти означает, что запрет есть.

Совершенно не означает.

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

Означает ли это, что для машин есть запрет на пересечение реки в этом месте?

Думаю, сами понимаете абсурдность такой трактовки.

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

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

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

Значит, какая разница в наблюдаемом поведении?

Никакой.

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

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

Я выше привёл пример с мостом через реку, который это объясняет.

про самоприсваивание вы слились вспомнили про шаблонные функции и в ответ на просьбу привести пример сказали «очень смешно».

Я не поддался на провокацию.
Что тут приводить?

Какая другая потребность различать объекты типа void у вас осталась?

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

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

Это — то, что первое пришло в голову, то есть, могут быть и более "впечатляющие" случаи, но уже одного этого достаточно.

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

Она настолько фундаментальная, что верна даже для функций.

Можно ссылку на это большинство?

Как вы себе это представляете?

Есть куда более сильная вещь, чем ссылка, — реальность.

Вы — в курсе, что предложения подобного рода уже были, и они не прошли?

Это и говорит о том, что "большинство считает".

Information

Rating
Does not participate
Registered
Activity