Comments 7
Примерно все компиляторы принудительно устанавливают размер пустой структуры или класса в 1 байт. Хоть этого нет в стандарте C++
Это есть в стандарте, чтобы не испортилась адресная арифметика
C++20 (ISO/IEC JTC1 SC22 WG21 N 4860)
6.7.2 Object model
An object has nonzero size if it
— is not a potentially-overlapping subobject, or
— is not of class type, or
— is of a class type with virtual member functions or virtual base classes, or
— has subobjects of nonzero size or bit-fields of nonzero length.
Otherwise, if the object is a base class subobject of a standard-layout class type with no non-static data members, it has zero size. Otherwise, the circumstances under which the object has zero size are implementation-defined. Unless it is a bit-field (11.4.9), an object with nonzero size shall occupy one or more bytes of storage, including every byte that is occupied in full or in part by any of its subobjects. An object of trivially copyable or standard-layout type (6.8) shall occupy contiguous bytes of storage.
Данная статья содержит фрагменты кода с UB.
1. Тут: operator VALUE() const { auto owner = reinterpret_cast<const OWNER *>(this); // <- Ключевой элемент return (owner->*GETTER)(); }
reintepret_cast в данном контексте не меняет то, на какой объект указывает указатель(https://eel.is/c++draft/expr.reinterpret.cast#7). И в связи с этим, указатель продолжает указывтаь на Property. Чтение же объекта из указателя другого типа нелегально(Ссылка на стандарт: https://eel.is/c++draft/basic.lval#11). Исправить ситуацию в данном случае, можно было бы при помощи std::launder(Конечно же с исправлением остальных UB). В коде в целом везде где reinterpret_cast используется, он используется неправильно.
2. Вариант с union, а не с атрибутом на [[no_unique_address]], так-же будет с UB. В связи с тем, что https://eel.is/c++draft/basic.life#7.2 требует, чтобы указатель указывал на объект для вызова нестатических методов. https://eel.is/c++draft/class.mem#general-5.sentence-2 Тут же указывается, что любая функция в классе - нестатический метод. https://eel.is/c++draft/basic.life#1 Согласно же этому, в union на момент использования, нет объекта.
3. auto owner = reinterpret_cast<const OWNER *>(this - OFFSET());
Тут так-же UB. Нет никаких правил в стандарте, которые бы разрешали бы тут pointer arithmetic, а по умолчанию она нелегальна.
По итогу остаётся то, что единственное, что тут можно сделать, это атрибут [[no_unique_address]], реинтерпрет каст + std::launder. Так и это достаточно ограниченно будет, но сделать через if constexpr другое, не такое Zero Overhead поведение для msvc в целом можно.
В общем в целом с такими вещами в плюсах нужно работать весьма осторожно, очень высок риск допустить ошибку, что и произошло в статье. Не стоит игра свечь и лучше взять или классические сеттеры и геттеры, или ссылку на себя передать.
В целом, ещё есть не лучшие моменты в статье. В типе Property, можно было сделать специализацию, дабы не писать руками возвращаемые типы при создании такового.
К слову, компиляторы Microsoft имеют способ описать property
Как и clang с -fdeclspec
.
Zero-cost Property в С++