Я (заново?) Изобрел этот подход к свойствам с нулевой стоимостью с помощью синтаксиса члена данных.Под этим я подразумеваю, что пользователь может написать:
some_struct.some_member = var;
var = some_struct.some_member;
, и эти обращения к членам перенаправляют на функции-члены с нулевыми издержками.
Хотя первоначальные тесты показывают, что подход работает на практике, яЯ далеко не уверен, что он свободен от неопределенного поведения.Вот упрощенный код, который иллюстрирует подход:
template <class Owner, class Type, Type& (Owner::*accessor)()>
struct property {
operator Type&() {
Owner* optr = reinterpret_cast<Owner*>(this);
return (optr->*accessor)();
}
Type& operator= (const Type& t) {
Owner* optr = reinterpret_cast<Owner*>(this);
return (optr->*accessor)() = t;
}
};
union Point
{
int& get_x() { return xy[0]; }
int& get_y() { return xy[1]; }
std::array<int, 2> xy;
property<Point, int, &Point::get_x> x;
property<Point, int, &Point::get_y> y;
};
Тестовый драйвер демонстрирует, что подход работает и он действительно не требует затрат (свойства не занимают дополнительную память):
int main()
{
Point m;
m.x = 42;
m.y = -1;
std::cout << m.xy[0] << " " << m.xy[1] << "\n";
std::cout << sizeof(m) << " " << sizeof(m.x) << "\n";
}
Реальный код немного сложнее, но суть подхода здесь.Он основан на использовании объединения реальных данных (xy
в этом примере) и пустых объектов свойств.(Реальные данные должны быть стандартным классом макета, чтобы это работало).
Объединение необходимо, потому что в противном случае свойства без необходимости занимают память, несмотря на то, что они пусты.
Почему я думаю, что здесь нет UB?Стандарт разрешает доступ к общей начальной последовательности членов объединения стандартных макетов.Здесь общая начальная последовательность пуста.К элементам данных x
и y
вообще нет доступа, так как нет элементов данных.Мое чтение стандарта указывает на то, что это разрешено.reinterpret_cast
должно быть в порядке, потому что мы приводим член объединения к содержащему объединению, и они являются взаимозаменяемыми по указателю.
Это действительно разрешено стандартом, или я здесь пропускаю некоторый UB?