简体   繁体   中英

Using Struct Members As Array, The Portable Way?

I have the following struct (lets take this case with 1 type as a simple case):

struct Vector {
    double x_, y_, z_;
};

According to C/C++ Pointer to a POD struct also points to the 1st struct member it seems that, in general, given there is no padding, you can write something like this:

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

And it should print out 32, 42, 68, in most cases, however as a comment in response to the marked answer on this question remarks, the struct may have padding, and this may not be portable. What would happen if the struct were written like this however?

struct alignas( double ) Vector {
    double x_, y_, z_;
};

And I were to write a functor like this

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

Because now there is explicit alignment, to the type specified ( double ), does that mean that the following code is not undefined behavior, is portable, and has a guaranteed output according to the C++20 standard?

int main() {
    Vector v{ .x_ = 23, .y_ = 43, .z_ = 64 };
    std::cout << GetComponent< VectorComponent::Z >( v ) << "\n";
    return 0;
}

Also since the the concept of POD's has changed, what about previous C++ standards (that included the idea of POD and alignas )?

Regardless of the memory layout of your struct , pointer arithmetic forbids this strategy. The relevant rule in this case is ( link ) :

The built-in subscript expression E1[E2] is exactly identical to the expression *(E1 + E2) [except evaluation order (since C++17)], that is, the pointer operand (which may be a result of array-to-pointer conversion, and which must point to an element of some array or one past the end ) is adjusted to point to another element of the same array, following the rules of pointer arithmetics, and is then dereferenced.

The members of your struct are not part of an array so you may not derive a pointer to one of them from a pointer to another.

In some cases, the rules will allow you to get a pointer that is both non-dereferencable and also coincidentally holds the address of an actual object of the same type. This can occur with one-past-the-end pointers. But even in these cases you cannot use that pointer to access the object at that address. How you get a pointer matters. If you get it illegally you can break assumptions the compiler made leading to unexpected behavior. These are assumptions that the compiler would have been allowed to make based on the belief that your code would not break the rules of the language.

it seems that, in general, given there is no padding, you can write something like this:

It might seem so, but we cannot because the behaviour of the pointer arithmetic is undefined.

What would happen if the struct were written like this however?

 struct alignas( double ) Vector

No difference. The class already had that alignment requirement, and the alignment has would have no effect on the validity of the pointer arithmetic anyway.

And I were to write a functor like this

That functor still has UB for the same reason, so it is not useful.


Using Struct Members As Array, The Portable Way?

The portable way is to use an array as an array:

struct Vector : std::array<double, 3> {};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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