【问题标题】:Anonymous union and struct [duplicate]匿名联合和结构
【发布时间】:2014-10-21 22:29:57
【问题描述】:

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

【问题讨论】:

  • 如果你坚持能写vec&lt;int&gt; t; t.x = 10;那么你可以给它引用成员xyz等,并初始化它们引用@的对应成员987654326@,但这会大大增加结构的大小。如果你能容忍t.x() = 10,那就让它们成为成员函数。
  • 但我认为vec.x = 42; assert(vec.r == 42); 无论如何都不是标准的保证(但编译器可能)。
  • @Jarod42 N3936 [class.mem]/18:“如果标准布局联合包含两个或多个共享公共初始序列的标准布局结构,并且如果标准布局联合对象当前包含这些标准布局结构之一,允许检查其中任何一个的公共初始部分。..."
  • 为了像使用非标准匿名结构一样访问成员,请为您的结构命名,就像 Praetorian 告诉您的那样,并在您的模板类型中为匿名结构的每个成员定义一个引用: struct vec{ ..... some_type &amp;x=a.x; ...};你可以参考vx而不是vax

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


【解决方案1】:

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

§9.5/5 [class.union]

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

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

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 条)、此类类型的数组 这些类型 (3.9.3) 的和 cv 限定版本统称为 标准布局类型

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

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

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

【讨论】:

  • 该数组不是标准布局结构。
  • @T.C.我刚刚意识到,当我在发布后重新阅读时(以前应该这样做)。如果类型是标准布局,则试图找到说它是的东西似乎很愚蠢,但不能保证数组是。
  • @Praetorian 是否标准保证elements[0] 将与a.x(和b.r)相同?找不到。
  • @Praetorian 哪里说数组和结构是布局兼容的?
  • @Praetorian §3.9/9 没有说明,它只是定义了什么是标准布局类型。此外,第 9.2/19 节指出“因此,在标准布局结构对象中可能存在未命名的填充,但不是在其开头,这是实现适当对齐所必需的”。事实上,它在某些情况下被插入,see the code
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

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

    换句话说,问题实际上是“我如何以符合标准的方式定义类型vec,以便给定该类型的对象uu.xu.r 和@987654326 @都指同一个东西?”

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

    所以:

    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&lt;double&gt; v;v.x 仍然是double &amp; 类型,因此您可以编写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 是(这使一些优化),它是一个聚合,因此可以使用聚合初始化语法,它也是 const 正确的。

    Demo。请注意,sizeof(vec&lt;double&gt;) 第一个版本为 72,第二个版本仅为 24。

    【讨论】:

    • 这也是最安全的方法,因为与 union 不同,它保证 x() == elements[0]
    猜你喜欢
    • 1970-01-01
    • 2012-07-13
    • 2019-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-14
    • 1970-01-01
    相关资源
    最近更新 更多