Aliasing struct and array the C++ way

后端 未结 4 1035
北海茫月
北海茫月 2020-12-05 09:45

This is a C++ followup for another question of mine

In the old days of pre-ISO C, the following code would have surprised nobody:

struct Point {
             


        
相关标签:
4条回答
  • 2020-12-05 10:21

    Use an constexpr array of pointer-to-member:

    #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);
    }
    
    0 讨论(0)
  • 2020-12-05 10:26

    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. 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

    0 讨论(0)
  • 2020-12-05 10:29

    IMHO the easiest way is to just implement 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;
    }
    
    0 讨论(0)
  • 2020-12-05 10:36
    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 doubles in your `struct. Asserting that is difficult.

    A POD struct is a sequence of bytes guaranteed.

    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.

    0 讨论(0)
提交回复
热议问题