n. *_* m. 18 c++ union undefined-behavior language-lawyer
我(re?)用数据成员语法发明了这种零成本属性的方法.通过这个我的意思是用户可以写:
some_struct.some_member = var;
var = some_struct.some_member;
Run Code Online (Sandbox Code Playgroud)
并且这些成员访问重定向到成员函数,零开销.
虽然初步测试表明该方法在实践中确实有效,但我很难确定它没有未定义的行为.以下是说明该方法的简化代码:
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;
};
Run Code Online (Sandbox Code Playgroud)
测试驱动程序演示了该方法的工作原理并且确实是零成本(属性不占用额外的内存):
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";
}
Run Code Online (Sandbox Code Playgroud)
真正的代码有点复杂,但方法的要点就在这里.它基于使用真实数据(xy在此示例中)和空属性对象的并集.(实际数据必须是标准布局类才能实现).
需要联盟,因为否则属性不必占用内存,尽管是空的.
为什么我认为这里没有UB?该标准允许访问标准布局联合成员的公共初始序列.这里,公共初始序列是空的.的数据成员x,并y没有在所有被访问的,因为没有数据成员.我对标准的阅读表明这是允许的.reinterpret_cast应该没问题,因为我们正在将一个联盟成员转换为其包含的union,这些是指针可互换的.
标准是否允许这样做,或者我在这里错过了一些UB?
Pas*_* By 14
TL; DR这是UB.
类似地,在对象的生命周期开始之前但在对象将占用的存储已经被分配之后,或者在对象的生命周期结束之后并且在对象占用的存储被重用或释放之前,任何引用的glvalue之前可以使用原始对象,但仅限于有限的方式.对于正在构建或销毁的对象,请参阅[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);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
680 次 |
| 最近记录: |