bestsource

C++ 방식으로 앨리어싱 구조 및 배열

bestsource 2023. 10. 16. 21:58
반응형

C++ 방식으로 앨리어싱 구조 및 배열

질문에 대한 C++ 후속 조치입니다.

ISOC 이전 시대에는 다음과 같은 코드가 아무도 놀라게 하지 않았을 것입니다.

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 값을 가진다)는 0 <= i + j <= n인 경우, (possibly hypot적인) 요소 x[i + j]를 가리키고, 그렇지 않으면 동작이 정의되지 않습니다.

그리고 (비규범적) 노트 86은 이를 더욱 명확하게 합니다.

배열 요소가 아닌 개체는 단일 요소 배열에 속하는 것으로 간주됩니다.n개 요소의 배열 x의 마지막 요소를 지나는 포인터는 이 목적을 위해 가상 요소 x[n]에 대한 포인터와 동일한 것으로 간주됩니다.

참고한 문항의 정답은 C언어가 조합을 통한 유형 펀닝을 수용한다는 점을 이용한 것이지만, C++ 표준에서는 결코 동등한 것을 찾을 수 없었습니다.그래서 저는 익명의 구조 구성원과 배열을 포함한 조합이Undefined BehaviourC++—서로 다른 언어입니다...

질문:.

구조의 멤버를 C++에서 배열의 멤버인 것처럼 반복하는 적합한 방법은 무엇입니까?현재 (C++17) 버전에서 방법을 찾고 있지만 이전 버전의 솔루션도 환영합니다.

면책 사항:

분명히 같은 유형의 요소에만 적용되며 패딩은 간단한 방법으로 감지할 수 있습니다.assert다른 질문에서 보듯 패딩, 얼라인먼트, 혼합형은 여기서 내 문제가 아닙니다.

멤버 간 포인터의 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);
}

가장 쉬운 방법은 바로 구현하는 것입니다.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<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);
  }
};

이것은 그 사이에 포장이 없다는 것에 의존합니다.double당신의 struct에 s.그것을 주장하는 것은 어렵습니다.

POD 구조는 보장된 바이트 시퀀스입니다.

컴파일러는 컴파일할 수 있어야 합니다.[]원시 배열 액세스 또는 포인터 산술과 동일한 명령(또는 그 결여)까지.이 최적화 작업이 "너무 늦게" 수행되어 다른 최적화 작업이 수행되지 않는 문제가 있을 수 있으므로 성능에 민감한 코드를 다시 확인하십시오.

로 하는 것은 합니다.char*아니면std::byte*uintptr_t유효하겠지만, 이 경우 포인터 산술이 허용되는지에 대한 핵심 문제가 있습니다.

에 할 수 .intptr_t산술을 한 다음 값을 포인터 유형으로 다시 캐스팅하는 것이 구현 정의된 동작입니다.대부분의 컴파일러에서 작동할 것으로 생각합니다.

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

이 기술은 가장 효율적이며, 한 사용자가 테이블을 찾아보면 최적의 최적화 장치를 만들 수 없을 것 같습니다. 어셈블리-비교

언급URL : https://stackoverflow.com/questions/48463521/aliasing-struct-and-array-the-c-way

반응형