سؤال

I have a simple point struct that I would like to inherit:

struct PointXYZ
{
    double x, y, z;
};

class extendedPoint : public PointXYZ
{
public:
    double getXYZ(int i);
    void setXYZ(int i, double value);
private:
    int moreproperties;
};

The get-set function is indexed because I'd like to be able to loop through getting and setting the xyz values. I'd like to link the concept of indexed xyz and standalone x,y,z by modifying my base class as described in the answer to this post:

struct PointXYZ
{
    union {
        struct {
            double x, y, z;
        };
        double xyz[3];
    };
};

class extendedPoint : public PointXYZ
{
public:
    double getXYZ(int i) { return xyz[i]; }
    void setXYZ(int i, double value) { xyz[i] = value; }
private:
    int moreproperties;
};

But this post directly contradicts the first post on whether the following is valid:

double dostuff()
{
    PointXYZ p;
    p.x = 123.88;
    return p.xyz[0];
}

So, is PointXYZ's use of union valid c++ and consistent between compilers or not?

هل كانت مفيدة؟

المحلول

From the standard:

In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time. [ Note: One special guarantee is made in order to simplify the use of unions: If a standard-layout union contains several standard-layout structs that share a common initial sequence (9.2), and if an object of this standard-layout union type contains one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of standard-layout struct members; see 9.2. — end note ]

Seems to me that the special guarantee indicates that

struct PointXYZ {
    union {
        struct {
            double xyz[3];
        };
        struct {
            double x, y, z;
        };
    };
};

should work just fine when reading the member that was not last written (See it happen).

Note, however, that according to the specs, your example of

struct PointXYZ {
    union {
        struct {
            double xyz[3];
        };
        double x, y, z;
    };
};

is undefined behaviour when reading the member that was not last written (See it happen). When setting xyz[0], xyz[1] and xyz[2], all of x, y and z have the same value as xyz[0] in the linked example. I would expect most compilers to behave like this, but the standard does not guarantee it.

نصائح أخرى

struct PointXYZ {
  union {
    double xyz[3];
    struct {
      double x, y, z;
    };
  };
};

Depending on the compiler / platform, you may have data alignment issues (it's possible though unlikely).

x, y, z may not reside in memory in correspondence with xyz[0], xyz[1], xyz[2].

C++ guarantees that elements of the xyz array are laid out contiguously, but it only guarantees that the address of an element of the struct is greater than the address of all earlier-declared elements (of course many compilers offer #pragma to control packing and alignment).

About the type punning matter (the practice of reading from a different union member than the one most recently written to)... you could say that this isn't type punning (types match, so there is no pun, it's merely aliasing), but still, from an the viewpoint of the language standard, the member written to and read from are different, which is undefined in C++.

Anyway, generally, you'll have the expected result...

With GCC it's even guaranteed to work (even with -fstrict-aliasing):

As a side note consider that anonymous unions are permitted but C++ prohibits anonymous structs (a lot of compilers allow them).

What about the following solution?

class PointXYZ
{
public:
  double getXYZ(unsigned i) { return xyz[i]; }
  void setXYZ(unsigned i, double value) { xyz[i] = value; }

  double &x() { return xyz[0]; }
  double &y() { return xyz[1]; }
  double &z() { return xyz[2]; }

private:
  double xyz[3];
};
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top