двадцать лет мифического человекоопыта
решил я сегодня утром попить кофейку вкусного.
и что вы думаете.
std::launder<T>.
– и кофе растянулся на час.

в прошлом году мы тут уже обсуждали ужасы object lifetime и uninitialized memory.
и вот – Gloria in excelsis Deo – механизм std::launder спешит нам всем на помощь,
продолжая делить людей на тех, кто про шашечки, и тех, кто про ехать.
я, как любитель хорошей резины на колёсах, в целом рад за обе стороны,
и, главное, за движуху.
@Zeux: Для тех кому лень искать:
template <class T> constexpr T* launder(T* p) noexcept;
Requires: p represents the address A of a byte in memory, and an object X
that is within its lifetime and whose cv-unqualified type is similar to T
is located at the address A.
Returns: A value of type T* that points to X.
struct X { const int n; };
X *p = new X{3};
const int a = p->n;
new (p) X{5}; // p does not point to new object
// because X::n is const
const int b = p->n; // undefined behavior
const int c = std::launder(p)->n; // ok
кошмарная жуть. хорошо, что об этом не надо думать.
@aruslan: да, это пришло изнутри комитета по уничтожению undefined behavior в С++.
я только их читаю.
если с вкусным кофе.
@Zeux: Крутой метод уничтожения undefined behavior!
@aruslan: ну, метод везде один и тот же.
improvise, modify, adapt and overcome.
или, по-русски – обнаружить, сформулировать, ограничить, выкинуть.
канализация, одним словом.
@Zeux: вот тут пока что по-моему только первые два
@aruslan: да.
они канализируют, концентрируют, и дают этому имена.
еще и раскрашивают, по-моему, в разные цвета.
но – главное – начинают выяснять кто конкретно и когда будет неправ если вообще будет.
а потом, зажмурившись, отрезают весь концентрат целиком
вместе с тем, что стояло рядом.
задача – сделать то, что рядом – очень чистым, ясным и очевидным
что туда ходить не надо.
@Vladimir Kananovich: не понимаю – зачем. c++ был очень крут
и все его флоз – стали причиной для появления массы языков
@aruslan: компиляторы пошли очень мощные и очень умные.
практически такие же умные, как мощные.
и почти в два раза более мощные чем отладчики.
и когда такой очень умный и более мощный чем отладчик компилятор берет твою программу,
она обычно превращается в тыкву.
и дальше все начинают кричать, что clang/gcc/vc++ белены объелись и так нельзя.
и что, например, сдвиг влево на один бит никак не может сделать нечётное число.
@Vladimir Kananovich: а что, может разве?
@aruslan: может. но узнаёшь об этом обычно только наутро
после посвященного изучению крэшдампов дня.
например вот такой вот С++:
int *a = ..., *b = ...;
ptrdiff_t result = (b - a) << 1;
внимание, вопрос:
может ли result быть нечётным?
ответ – конечно же может.
если указатели a и b – unrelated.
@Zeux: если они unrelated то вычитать нельзя?
@aruslan: нельзя.
сравнивать тоже нельзя.
@Zeux: т.е. проблема не в том, что << 1 может вернуть нечётный бит
а в том что может случиться вообще всё
@aruslan: вот именно.
просто пользователь видит не “вообще всё”,
а внезапно – нечётный результат,
обычно после проведенной за раскруткой стеков ночи.
и думает, что его обманули.
@Vladimir Kananovich: я ожидал поочередных сдвигов и потерю проблемы
после деления на 4
@aruslan: и был бы совершенно прав!
но ты думаешь о компиляторе как о калькуляторе.
который заведомо тупее тебя.
а компилятор смотрит на твой код как на материал для изготовления тыквы.
он видит, что делит расстояние в байтах на sizeof(int) (то есть делает >>2),
а потом умножает на 2 (<<1).
– и сокращает >>2 <<1 в один >>1.
одна инструкция вместо двух – красиво.
если указатели related (из одного массива например) – всё отлично.
если нет – то код и так уже тыква, и результат может быть нечётным.
не потому что компилятор “сломался”.
а потому что ты был неправ уже тогда
когда выбрал С++.
компилятор этим просто воспользовался.
@Vladimir Kananovich: но пример нерелевантен (прошу прощения)
потому как есть еще 1001 кейз где можно себе в ногу попасть
по-хорошему все кто уже отстрелил себе все пятки не будут равнять указатель на середину инта
потому как трудно потом забукать время на 3 дня борьбы с сайд эфектом
@aruslan: эх, но нет: когда компилятор становится умнее 99% разработчиков на С++
(то есть практически сразу же как только он научается компилировать С++)
нужно переставать верить вообще любой строчке кода.
@Vladimir Kananovich: привыкаешь не делать глупостей
например после сдвига кладешь маску и больше не возвращаешься..
и пофиг clang или MS
@aruslan: ты совершенно прав, и говоришь как старый пердун типа меня
неужели ты всерьез думаешь что маска поможет.
компилятор чай тоже не индюк: он-то точно знает что разность – целое число,
и после сдвига влево – может быть только четной,
и поэтому маска которая делает четное число еще более четным – как мертвому припарка.
это если оптимизатор мощный, но я бы не мерялся!
впрочем, ты прав, на gcc маска еще помогает: [годболт].
а вот на clang уже нет: [годболт]:
subq a(%rip), %rsi
sarq %rsi
то есть gcc еще слабее нас стариков, а вот clang уже смотрит на нас как на тыкву.
и вот std::launder – это тот же самый случай,
просто не в арифметике указателей,
а во времени жизни объектов.
человек думает: “я аккуратно, я placement new, я знаю что делаю”.
а стандарт говорит: “эй, ты слышишь полночные куранты?”.
компилятор – не калькулятор.
он не обязан быть снисходительным.
@Vladimir Kananovich: компильнул на убунте смотрю objdump – тоже sar – вправо.
на самом деле конечно обидно, что десятки лет опыта сегодня уже не продашь
@aruslan: не за опыт обидно.
опыт – это не годы, это количество раз, когда ты понял, где именно тебя обманули.
обидно за то, что цена ошибки упала.
проще поэкспериментировать, чем подумать.
проще спросить stack overflow,
где такие же, как ты, коллективно дотянутся до правильного ответа.
crowd sourcing работает.
apprenticeship – тоже работает.
а вот институт лесозаготовительный факультет искусствоведения
“я 20 лет пишу C++ и оно до сих пор” – не работает.
@Val Krylov: Вот кстати, ведь где-то в стандарте просвечивала такая шиза.
И значит ли это, что после залезания очередного бешеного красноглазика в компиляторы
все std::set и std::map на T* накроются медным тазом?
@aruslan: нет, не накроются, они не используют a - b или там a < b.
они используют std::less<T*>(a, b), который гарантирует total order для указателей.
даже там, где встроенный < не гарантирует.
иначе всё поросло бы уже очень давно –- unrelated pointers comparison не был валиден никогда.
@Zeux: прекрасный язык
@aruslan: только так
@Vladimir Kananovich: самое больное – как заставить молодого человека 20 лет
полгода не зарабатывать с с++,
когда он уже с пхп может зарабатывать
@aruslan: правильная проблема – как сделать так чтобы через двадцать лет
работы с С++
на тебя смотрели как на профи
и платили ОЧЕНЬ МНОГО ДЕНЕГ.
потому что на пхп через двадцать лет ты будешь все равно просто пхп.
@Vladimir Kananovich: бизнесу наплевать на флажки, гонки и переполнения
@aruslan: совершенно верно. бизнесу плевать на С++.
пока ему не скажут что если ему надо 100500 ABCD/s
то надо использовать Толстого Жирного Лысого дядьку в Очках
и тот сделает приятно и больно одновременно
за Очень Много Денег
на С++.
и если бизнес поймет что 100500 ABCD/s –
это нижний порог на рынке куда он ломится
– то будет счастье.
но счастья не будет,
если Толстый Жирный дядька выдаёт ровно ту же неработающую тыкву,
что и Прыщавый Подросток в Пубертате.
потому что видите ли компиляторы стали нынче умные.
важно чтобы были позиции где можно подойти к подросткам,
осмотреть издалека их творение,
подойти целенаправленно поближе,
тыкнуть пальцем, и,
наблюдая как всё падает вокруг и опорные конструкции гнутся,
и пот и кровь и мозги и кишки,
зевнуть и спросить задумчиво:
– а вы кстати вот это что тут стояло –- вы его под xSAN запускали?
или например внезапно подключиться и сделать пять снапшотов стека.
и поинтересоваться:
– а вы уверены, что sscanf() в пяти рандомных снапшотах стека –- это признак производительности?
но это я конечно утрирую, хоть все примеры и из жизни.
более приземлённые случаи – они исключительно в навыках работы со static_cast и его std::move итп разновидностями.
там люди очень часто отрывают не только ноги, и не только себе.
так что разница между “спецом” и “нет” сегодня – это не количество лет,
а наличие карты минного поля.
не для того, чтобы по нему ходить.
а чтобы знать, где оно вообще есть.
вот для этого и нужны все эти std::launder,
std::kill_dependency,
std::forward
и прочие странные, неприятные штуки.
они не делают код красивым.
они делают границы поля видимыми.
как в Марракеше.
город в целом безопасный,
но есть места, куда не заходят.
очень рекомендую, кстати, ресторан Aziza в Сан-Франциско.
без понтов,
без деконструкции макарон,
просто очень вкусно.
как и везде с С++.
надо чаще встречаться!