C++ 使用结构成员作为数组,可移植的方式?

C++ 使用结构成员作为数组,可移植的方式?,c++,C++,我有以下结构(让我们把这个1类型的例子作为一个简单的例子): 根据它的说法,一般来说,如果没有填充,你可以这样写: int main() { Vector v{ 32.0, 42.0, 68.0 }; double* d = &v.x_; for( int i = 0; i < 3; ++i ){ std::cout << d[ i ] << ", "; } return 0; }

我有以下结构(让我们把这个1类型的例子作为一个简单的例子):

根据它的说法,一般来说,如果没有填充,你可以这样写:

int main() 
{
    Vector v{ 32.0, 42.0, 68.0 };
    double* d = &v.x_;
    for( int i = 0; i < 3; ++i ){
        std::cout << d[ i ] << ", ";
    }
    return 0;
}
我要写一个像这样的函子

//VectorComponent is an enum class, I will spare the reader the details.//
template< VectorComponent ComponentSelectorConstant >
struct GetComponent
{
    const double& ComponentConstant;
    GetComponent( const Vector& vector ) : 
            ComponentConstant( ( &vector.x_ )[ static_cast< size_t >( ComponentSelectorConstant ) ] ) {
    }
    operator const double&() {
        return ComponentConstant;
    }
};

也因为POD的概念发生了变化,以前的C++标准(包括POD和 >?

> P>),不管你的代码>结构> <代码>的内存布局,指针算法都禁止这种策略。本例中的相关规则是():

内置的下标表达式
E1[E2]
与表达式
*(E1+E2)
[除了求值顺序(自C++17以来)]完全相同,即指针操作数(可能是数组到指针转换的结果,必须指向某个数组的某个元素或超过该数组末尾的某个元素)根据指针算术的规则,调整为指向同一数组的另一个元素,然后取消引用

struct
的成员不是数组的一部分,因此不能从指向另一个的指针派生指向其中一个的指针


在某些情况下,这些规则将允许您获取一个指针,该指针既不可取消引用,又同时包含同一类型的实际对象的地址。当一个指针超过结束指针时,可能会发生这种情况。但即使在这些情况下,也不能使用该指针访问该地址处的对象。如何获得指针很重要。如果您非法获取它,您可能会破坏编译器做出的导致意外行为的假设。这些都是允许编译器基于代码不会违反语言规则的信念做出的假设。

无论
结构的内存布局如何,指针算法都禁止这种策略。本例中的相关规则是():

内置的下标表达式
E1[E2]
与表达式
*(E1+E2)
[除了求值顺序(自C++17以来)]完全相同,即指针操作数(可能是数组到指针转换的结果,必须指向某个数组的某个元素或超过该数组末尾的某个元素)根据指针算术的规则,调整为指向同一数组的另一个元素,然后取消引用

struct
的成员不是数组的一部分,因此不能从指向另一个的指针派生指向其中一个的指针

在某些情况下,这些规则将允许您获取一个指针,该指针既不可取消引用,又同时包含同一类型的实际对象的地址。当一个指针超过结束指针时,可能会发生这种情况。但即使在这些情况下,也不能使用该指针访问该地址处的对象。如何获得指针很重要。如果您非法获取它,您可能会破坏编译器做出的导致意外行为的假设。这些是允许编译器基于代码不会违反语言规则的信念而做出的假设

一般来说,如果没有填充,您可以这样写:

int main() 
{
    Vector v{ 32.0, 42.0, 68.0 };
    double* d = &v.x_;
    for( int i = 0; i < 3; ++i ){
        std::cout << d[ i ] << ", ";
    }
    return 0;
}
看起来可能是这样,但我们不能,因为指针算术的行为是未定义的

但是,如果结构是这样写的,会发生什么

struct alignas( double ) Vector {
    double x_, y_, z_;
};
struct alignas( double ) Vector
没有区别。该类已经具有该对齐要求,并且该对齐方式无论如何都不会影响指针算法的有效性

我要写一个像这样的函子

//VectorComponent is an enum class, I will spare the reader the details.//
template< VectorComponent ComponentSelectorConstant >
struct GetComponent
{
    const double& ComponentConstant;
    GetComponent( const Vector& vector ) : 
            ComponentConstant( ( &vector.x_ )[ static_cast< size_t >( ComponentSelectorConstant ) ] ) {
    }
    operator const double&() {
        return ComponentConstant;
    }
};
出于同样的原因,该函子仍然有UB,因此它没有用处


使用结构成员作为数组,可移植的方式

可移植的方法是将阵列用作阵列:

struct Vector : std::array<double, 3> {};
结构向量:std::array{};
一般来说,如果没有填充,您可以这样写:

int main() 
{
    Vector v{ 32.0, 42.0, 68.0 };
    double* d = &v.x_;
    for( int i = 0; i < 3; ++i ){
        std::cout << d[ i ] << ", ";
    }
    return 0;
}
看起来可能是这样,但我们不能,因为指针算术的行为是未定义的

但是,如果结构是这样写的,会发生什么

struct alignas( double ) Vector {
    double x_, y_, z_;
};
struct alignas( double ) Vector
没有区别。该类已经具有该对齐要求,并且该对齐方式无论如何都不会影响指针算法的有效性

我要写一个像这样的函子

//VectorComponent is an enum class, I will spare the reader the details.//
template< VectorComponent ComponentSelectorConstant >
struct GetComponent
{
    const double& ComponentConstant;
    GetComponent( const Vector& vector ) : 
            ComponentConstant( ( &vector.x_ )[ static_cast< size_t >( ComponentSelectorConstant ) ] ) {
    }
    operator const double&() {
        return ComponentConstant;
    }
};
出于同样的原因,该函子仍然有UB,因此它没有用处


使用结构成员作为数组,可移植的方式

可移植的方法是将阵列用作阵列:

struct Vector : std::array<double, 3> {};
结构向量:std::array{};
< /代码>即使没有填充,元素也完全是 SigeOf(double)彼此之间,指针运算规则仍然不允许您从指针指向另一个成员,而C++标准则不能提供任何保证。你想解决的真正问题是什么。不,不是将结构成员作为数组访问的问题,而是您认为解决方案涉及将结构成员作为数组访问的问题,所以这就是您要问的问题。也许如果你问真正的问题,一个更简单、更干净的解决方案是可能的,而不是这个。内存是否连续是无关紧要的。编译器遵照C++抽象机标准中所规定的规则。如果您的代码违反了规则,则它具有未定义的行为。编译器相信您的代码不会违反规则,并且可能会对无效代码做出不正确的优化假设,从而在最坏的情况下导致不良行为。要么会导致欢闹和滑稽,要么会导致产品发布前一天的深夜调试会议。其中一个问题是无意的UB非常普遍。而且事情通常按预期工作,这意味着它很少从旧代码中清除。可能追溯到C和C++的早期,但更为关键,可能会作为复杂的优化而出人意料。