c++ - 具有数据成员语法的零成本属性
问题描述
我已经(重新?)发明了这种使用数据成员语法的零成本属性方法。我的意思是用户可以写:
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?
解决方案
TL;DR 这是 UB。
类似地,在对象的生命周期开始之前但在对象将占用的存储空间已分配之后,或者在对象的生命周期结束之后并且在对象占用的存储空间被重用或释放之前,引用的任何泛左值可以使用原始对象,但只能以有限的方式使用。对于正在构造或销毁的对象,请参阅 [class.cdtor]。否则,这样的glvalue指的是分配的存储,并且使用不依赖于其值的glvalue的属性是明确定义的。如果出现以下情况,该程序具有未定义的行为:[...]
- glvalue 用于调用对象的非静态成员函数,或
根据定义,工会的非活动成员不在其生命周期内。
一种可能的解决方法是使用 C++20[[no_unique_address]]
struct Point
{
int& get_x() { return xy[0]; }
int& get_y() { return xy[1]; }
[[no_unique_address]] property<Point, int, &Point::get_x> x;
[[no_unique_address]] property<Point, int, &Point::get_y> y;
std::array<int, 2> xy;
};
static_assert(offsetof(Point, x) == 0 && offsetof(Point, y) == 0);
推荐阅读
- visual-studio-code - 启动非默认终端的快捷键?
- loops - Ansible:从多个堆栈中获取 instance_ids
- powershell - 事件 ID 计数和警告错误的 Powershell 命令
- python - 如何在 android studio 中使用 .h5 文件?
- c# - 如何从控制器更新 KendoUI Grids 数据源?
- python - Docker 健康检查总是返回不健康
- c++ - C ++中的线程函数参数问题
- django - 本地窗口 - 服务器 Ubuntu,git push live no putty
- node.js - 读取/设置套接字头信息 [Node.js]
- jhipster - 生成器-jhipster-blueprint “jhContext.setupClientOptions 不是函数”