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