简体   繁体   English

别名结构和数组C ++方式

[英]Aliasing struct and array the C++ way

This is a C++ followup for another question of mine 这是我的另一个问题的 C ++后续

In the old days of pre-ISO C, the following code would have surprised nobody: 在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);
}

Unfortunately, this problematic line uses pointer arithmetic ( p[i] being by definition *(p + i)) outside of any array which is explicitely not allowed by the standard. 不幸的是,这个有问题的行使用指针运算( p[i] 按定义 *(p + i))在任何数组之外,标准明确不允许这样做。 Draft 4659 for C++17 says in 8.7 [expr.add]: C ++ 17草案4659在8.7 [expr.add]中说:

If the expression P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i + j] if 0 <= i + j <= n; 如果表达式P指向具有n个元素的数组对象x的元素x [i],则表达式P + J和J + P(其中J具有值j)指向(可能是假设的)元素x [i + j]如果0 <= i + j <= n; otherwise, the behavior is undefined. 否则,行为未定义。

And the (non-normative) note 86 makes it even more explicit: 并且(非规范性)注释86使其更加明确:

An object that is not an array element is considered to belong to a single-element array for this purpose. 为此,不将数组元素的对象视为属于单元素数组。 A pointer past the last element of an array x of n elements is considered to be equivalent to a pointer to a hypothetical element x[n] for this purpose. 为了这个目的,超过n个元素的数组x的最后一个元素的指针被认为等同于指向假设元素x [n]的指针。

The accepted answer of the referenced question uses the fact that the C language accepts type punning through unions, but I could never find the equivalent in the C++ standard. 引用问题的公认答案使用了C语言通过联合接受类型惩罚的事实,但我无法在C ++标准中找到等价物。 So I assume that a union containing an anonymous struct member and an array would lead to Undefined Behaviour in C++ — they are different languages... 所以我假设包含匿名结构成员和数组的联合将导致C ++中的Undefined Behaviour - 它们不同的语言......

Question: 题:

What could be a conformant way to iterate through members of a struct as if they were members of an array in C++? 什么是一种一致的方式来迭代结构的成员,就好像它们是C ++中的数组成员一样? I am searching for a way in current (C++17) versions, but solutions for older versions are also welcome. 我正在寻找当前(C ++ 17)版本的方法,但也欢迎旧版本的解决方案。

Disclaimer: 免责声明:

It obviously only applies to elements of same type, and padding can be detected with a simple assert as shown in that other question , so padding, alignment, and mixed types are not my problem here. 它显然只适用于相同类型的元素,并且可以使用简单的assert检测填充,如其他问题所示,因此填充,对齐和混合类型不是我的问题。

Use an constexpr array of pointer-to-member: 使用指向成员的指针的constexpr数组:

#include <math.h>

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);
}

IMHO the easiest way is to just implement operator[] . 恕我直言,最简单的方法是实现operator[] You can make a helper array like this or just create a switch... 你可以像这样制作一个帮助器数组,或者只是创建一个开关......

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<uintptr_t>( this );
    auto v = self+i*sizeof(double);
    return *reinterpret_cast<double*>(v);
  }
  double const& operator[]( std::size_t i ) const {
    auto self = reinterpret_cast<uintptr_t>( this );
    auto v = self+i*sizeof(double);
    return *reinterpret_cast<double const*>(v);
  }
};

this relies on there being no packing between the double s in your `struct. 这取决于你的`struct中的double s之间没有打包。 Asserting that is difficult. 断言这很难。

A POD struct is a sequence of bytes guaranteed. POD结构是保证的字节序列。

A compiler should be able to compile [] down to the same instructions (or lack thereof) as a raw array access or pointer arithmetic. 编译器应该能够将[]编译为与原始数组访问或指针算法相同的指令(或缺少指令)。 There may be some problems where this optimization happens "too late" for other optimzations to occur, so double-check in performance sensitive code. 可能存在一些问题,其中这种优化对于其他优化发生“太晚”,因此请仔细检查性能敏感代码。

It is possible that converting to char* or std::byte* insted of uintptr_t would be valid, but there is a core issue about if pointer arithmetic is permitted in this case. 转换为uintptr_t char*std::byte* insned可能是有效的,但是在这种情况下是否允许指针算术存在核心问题

You could use the fact that casting a pointer to intptr_t doing arithmetic and then casting the value back to the pointer type is implemetation defined behavior . 您可以使用以下事实:将指针intptr_t执行算术的intptr_t然后将值转换回指针类型是实现定义的行为 I believe it will work on most of the compilers: 我相信它适用于大多数编译器:

template<class T>
T* increment_pointer(T* a){
  return reinterpret_cast<T*>(reinterpret_cast<intptr_t>(a)+sizeof(T));
  }

This technic is the most efficient, optimizers seems not to be able to produce optimal if one use table look up: assemblies-comparison 这种技术是最有效的,如果一个使用表查找,优化器似乎无法产生最佳: 程序集 - 比较

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM