匿名联合和结构

时间:2014-08-28 06:46:43

标签: c++ c++11 struct unions

您将如何在标准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];
    };
};

3 个答案:

答案 0 :(得分:12)

是的,C ++ 11和C ++ 14都不允许使用匿名结构。 This answer包含一些推理原因。您需要命名结构,它们也不能在匿名联合中定义。

§9.5/ 5 [class.union]

  

...匿名工会的成员规范只能定义非静态数据成员。 [注意:无法在匿名联合中声明嵌套类型,匿名联合和函数。 -end note ]

所以将结构定义移到联合之外。

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标准布局,因为这会使匿名联盟的所有成员布局兼容Here are the requirements表示标准布局类型。这些在标准的第9/7节中描述。​​

然后,从§9.2[class.mem]

  

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

对于数组成员,来自§3.9/ 9 [basic.types]

  

...标量类型,标准布局类类型(第9节),此类型的数组   和cv限定版本的这些类型(3.9.3)统称为标准布局类型

要确保some_type是标准布局,请在vec

的定义中添加以下内容
static_assert(std::is_standard_layout<some_type>::value, "not standard layout");

std::is_standard_layouttype_traits标头中定义。现在联盟的所有3个成员都是标准布局,两个结构和数组是布局兼容的,因此3个联合成员共享一个共同的初始序列,这允许您编写然后检查(读取)属于该公共的任何成员初始序列(在你的情况下整个事情)。

答案 1 :(得分:8)

C ++ 11/14允许使用匿名联合。请参阅Bjarne Stroustrup's C++11 FAQ

中的用法示例

关于匿名结构,请参阅Why does C++11 not support anonymous structs, while C11 does?Why does C++ disallow anonymous structs and unions?

虽然大多数编译器都支持匿名结构,但如果您希望代码符合标准,则必须编写如下内容:

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

答案 2 :(得分:6)

我认为其他答案有点错过了问题:

  

我希望以与此相同的方式访问成员。

换句话说,问题实际上是“我如何以符合标准的方式定义类型vec,以便给定该类型的对象u u.x,{ {1}},而u.r都是指同一件事?“

好吧,如果你坚持这种语法......那么显而易见的答案是:引用。

所以:

u.elements[0]

这种方法的第一个问题是你需要6个参考成员的额外空间 - 这对于这样一个小结构来说相当昂贵。

此方法的第二个问题是,给定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]; }; 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(),但节省的空间相当大,您还可以依赖编译器生成的特殊成员函数,如果{{1,则可以轻松复制是(它允许一些优化),它是一个聚合,所以可以使用聚合初始化语法,它也是const-correct。

Demo。请注意,第一个版本u.x为72,第二个版本仅为24。