匿名联合和结构

Anonymous union and struct

本文关键字:结构      更新时间:2023-10-16

在标准C++11/14中,您将如何做到这一点?因为若我并没有记错的话,这不是匿名结构的标准兼容代码。

我希望以与你同样的方式接触各位成员。

template <typename some_type>
struct vec
{
    union {
        struct { some_type x, y, z; };
        struct { some_type r, g, b; };
        some_type elements[3];
    };
};

是的,C++11和C++14都不允许匿名结构。这个答案包含了为什么会出现这种情况的一些理由。您需要命名结构,而且它们也不能在匿名联合中定义。

§9.5/5[阶级联合]

...匿名联合的成员规范应仅定义非静态数据成员。注意:嵌套类型、匿名联合和函数不能在匿名联合中声明

因此,将结构定义移动到并集之外。

template <typename some_type>
struct vec
{
    struct xyz { some_type x, y, z; };
    struct rgb { some_type r, g, b; };
    union {
        xyz a;
        rgb b;
        some_type elements[3];
    };
};

现在,我们要求some_type标准布局,因为这使得匿名联合布局的所有成员都兼容。以下是标准布局类型的要求。本标准第9/7节对此进行了说明。

然后,从§9.2[class.mem]

16;如果两个标准布局结构(第9条)类型具有相同数量的非静态数据成员,并且相应的非静态的数据成员(按声明顺序)具有布局兼容的类型(3.9),则它们是布局兼容的。
18;如果标准布局联合包含共享公共初始序列的两个或多个标准布局结构,并且标准布局联合对象当前包含这些标准布局结构中的一个,则允许检查其中任何一个的公共初始部分。如果对应的成员具有布局兼容的类型,并且两个成员都不是位字段,或者对于一个或多个初始成员的序列,两个标准布局结构共享一个公共的初始序列

对于数组成员,从§3.9/9[基本类型]

...标量类型、标准布局类类型(第9条)、此类类型的数组这些类型的cv合格版本(3.9.3)统称为标准布局类型

为了确保some_type是标准布局,在vec 的定义中添加以下内容

static_assert(std::is_standard_layout<some_type>::value, "not standard layout");

CCD_ 6被定义在CCD_。现在,并集的所有3个成员都是标准布局,两个structs和数组是布局兼容的,因此这3个并集成员共享一个公共初始序列,这允许您写入并检查(读取)属于公共初始序列的任何成员(在您的情况下是整件事)。

C++11/14中允许匿名联合。参见Bjarne Stroustrup的C++11常见问题中的使用示例

关于匿名结构,请参阅为什么C++11不支持匿名结构,而C11支持?为什么C++不允许匿名结构和联合?

尽管大多数编译器都支持匿名结构,但如果你想让你的代码符合标准,你必须写这样的东西:

template <typename some_type>
struct vec
{
    union {
       struct { some_type x, y, z; } s1;
       struct { some_type r, g, b; } s2;
       some_type elements[3];
    };
};

我认为其他答案有点忽略了问题的要点:

我希望以与你同样的方式接触各位成员。

换句话说,问题实际上是"我如何以符合标准的方式定义类型vec,使得给定该类型的对象uu.xu.ru.elements[0]都引用相同的东西?"

如果你坚持这个语法。。。那么显而易见的答案是:参考文献。

因此:

template <typename some_type>
struct vec
{
    vec() = default;
    vec(const vec& other) : elements{ other.elements[0], other.elements[1], other.elements[2] } {}
    vec & operator=(const vec &other) {
        elements[0] = other.elements[0];
        elements[1] = other.elements[1];
        elements[2] = other.elements[2];
        return *this;
    }
    some_type elements[3];
    some_type &x = elements[0], &y = elements[1], &z = elements[2];
    some_type &r = elements[0], &g = elements[1], &b = elements[2];    
};

这种方法的第一个问题是,您需要为6个引用成员提供额外的空间——对于这样一个小的结构来说,这是相当昂贵的。

这种方法的第二个问题是,给定const vec<double> v;v.x仍然是double &类型,因此您可以编写v.x = 20;并在没有警告或错误的情况下编译它,只会得到未定义的行为。相当糟糕。

因此,在另一种选择中,您可以考虑使用访问器函数:

template <typename some_type>
struct vec
{   
    some_type elements[3];
    some_type &x() { return elements[0]; }
    const some_type &x() const { return elements[0]; }
    some_type &y() { return elements[1]; }
    const some_type &y() const { return elements[1]; }
    some_type &z() { return elements[2]; }
    const some_type &z() const { return elements[2]; }
    some_type &r() { return elements[0]; }
    const some_type &r() const { return elements[0]; }
    some_type &g() { return elements[1]; }
    const some_type &g() const { return elements[1]; }
    some_type &b() { return elements[2]; }
    const some_type &b() const { return elements[2]; }
};

您必须编写u.x()等,而不是u.x,但节省的空间相当大,您还可以依赖编译器生成的特殊成员函数,如果some_type是(这允许一些优化),它是一个聚合,因此可以使用聚合初始化语法,而且它也是常量正确的。

演示。注意,sizeof(vec<double>)对于第一个版本是72,而对于第二个版本只有24。