使用C ++方式对结构和数组进行别名
这是我的另一个问题的 C ++后续
在ISO C之前的旧时代,以下代码会让人感到惊讶:
struct Point { double x; double y; double z; }; double dist(struct Point *p1, struct Point *p2) { double d2 = 0; double *coord1 = &p1->x; double *coord2 = &p2->x; int i; for (i=0; i<3; i++) { double d = coord2[i] - coord1[i]; // THE problem d2 += d * d; } return sqrt(d2); }
不幸的是,这个有问题的行使用指针运算( p[i]
按定义 *(p + i))
在任何数组之外,标准明确不允许这样做。 C ++ 17草案4659在8.7 [expr.add]中说:
如果表达式P指向具有n个元素的数组对象x的元素x [i],则表达式P + J和J + P(其中J具有值j)指向(可能是假设的)元素x [i + j]如果0 <= i + j <= n; 否则,行为未定义。
并且(非规范性)注释86使其更加明确:
为此,不将数组元素的对象视为属于单元素数组。 为了这个目的,超过n个元素的数组x的最后一个元素的指针被认为等同于指向假设元素x [n]的指针。
引用问题的接受答案使用了C语言通过联合接受类型惩罚的事实,但我永远无法在C ++标准中找到等价物。 所以我假设包含匿名结构成员和数组的联合将导致C ++中的Undefined Behaviour
– 它们是不同的语言……
题:
什么是一种一致的方式来迭代结构的成员,就好像它们是C ++中的数组成员一样? 我正在寻找当前(C ++ 17)版本的方法,但也欢迎旧版本的解决方案。
免责声明:
它显然只适用于相同类型的元素,并且可以使用简单的assert
检测填充,如其他问题所示,因此填充,对齐和混合类型不是我的问题。
使用指向成员指针的constexpr数组:
#include struct Point { double x; double y; double z; }; double dist(struct Point *p1, struct Point *p2) { constexpr double Point::* coords[3] = {&Point::x, &Point::y, &Point::z}; double d2 = 0; for (int i=0; i<3; i++) { double d = p1->*coords[i] - p2->*coords[i]; d2 += d * d; } return sqrt(d2); }
恕我直言,最简单的方法是实现operator[]
。 你可以像这样制作一个辅助数组,或者只是创建一个开关……
struct Point { double const& operator[] (std::size_t i) const { const std::array coords {&x, &y, &z}; return *coords[i]; } double& operator[] (std::size_t i) { const std::array coords {&x, &y, &z}; return *coords[i]; } double x; double y; double z; }; int main() { Point p {1, 2, 3}; std::cout << p[2] - p[1]; return 0; }
struct Point { double x; double y; double z; double& operator[]( std::size_t i ) { auto self = reinterpret_cast( this ); auto v = self+i*sizeof(double); return *reinterpret_cast(v); } double const& operator[]( std::size_t i ) const { auto self = reinterpret_cast ( this ); auto v = self+i*sizeof(double); return *reinterpret_cast(v); } };
这取决于你的`struct中的double
s之间没有打包。 断言这很难。
POD结构是保证的字节序列。
编译器应该能够将[]
编译为与原始数组访问或指针算法相同的指令(或缺少指令)。 可能存在一些问题,其中此优化发生“太晚”以使其他优化发生,因此请仔细检查性能敏感代码。
转换为uintptr_t
char*
或std::byte*
insned可能是有效的,但是在这种情况下是否允许指针算法存在核心问题 。
您可以使用以下事实:将指针intptr_t
执行算术的intptr_t
然后将值转换回指针类型是实现定义的行为 。 我相信它适用于大多数编译器:
template T* increment_pointer(T* a){ return reinterpret_cast(reinterpret_cast(a)+sizeof(T)); }
这种技术是最有效的,如果一个使用表查找,优化器似乎无法产生最佳: 程序集 – 比较