为什么C++不允许匿名结构?

j8ag8udp  于 2022-12-01  发布在  其他
关注(0)|答案(8)|浏览(226)

一些C编译器允许匿名联合和结构作为标准C的扩展。这是一点语法糖,有时非常有用。
是什么原因阻止它成为标准的一部分?是技术上的障碍?还是哲学上的障碍?或者只是没有足够的必要来证明它的合理性?
下面是我所说的一个例子:

struct vector3 {
  union {
    struct {
      float x;
      float y;
      float z;
    };
    float v[3];
  };
};

我的编译器将接受这一点,但它警告"nameless struct/union" is a non-standard extension to C++

ubby3x7f

ubby3x7f1#

正如其他人所指出的,标准C中允许匿名联合,但不允许匿名结构。
这是因为C支持匿名联合体,但不支持匿名结构体 *,所以C
支持前者是为了兼容,而不支持后者是因为它不是兼容所必需的。
而且,匿名结构在C中没有太大的用处。您演示的用法,即让一个结构包含三个浮点数,这些浮点数可以由.v[i].x.y.z引用,我相信这会导致C中的未定义行为。C不允许您写入联合的一个成员,比如.v[1],然后从另一个成员中读取,比如.y。虽然这样做的代码并不罕见,但实际上并没有很好地定义。
C
的用户定义类型工具提供了替代解决方案。例如:

struct vector3 {
  float v[3];
  float &operator[] (int i) { return v[i]; }
  float &x() { return v[0]; }
  float &y() { return v[1]; }
  float &z() { return v[2]; }
};
  • C11显然添加了匿名结构,所以C++的未来修订版可能会添加它们。
6qftjkof

6qftjkof2#

我会说,您可以通过使用union来清理vector3声明

union vector3 {
  struct { float x, y, z; } ;
  float v[3] ;
} ;

当然,anonymous structures was an MSVC extension。但是ISO C11现在允许它,gcc allows it,苹果的llvm编译器也允许。
为什么是C11而不是C11?我不确定,但实际上大多数(gcc、MSVC和Apple的C编译器)C++编译器都支持它们。

but5z9lq

but5z9lq3#

C++规范第9.5节第2条:
形式的并集

union { member-specification } ;

称为匿名联合;它定义了一个未命名类型的未命名对象。
你也可以这样做:

void foo()
{
  typedef
  struct { // unnamed, is that what you mean by anonymous?
    int a;
    char b;
  } MyStructType; // this is more of a "C" style, but valid C++ nonetheless

  struct { // an anonymous struct, not even typedef'd
    double x;
    double y;
  } point = { 1.0, 3.4 };
}

并不总是很有用...虽然有时在讨厌的宏定义中很有用。

cczfrluj

cczfrluj4#

联合可以是匿名的;参见标准9.5第2段。
你认为匿名结构体或类的用途是什么?在猜测为什么有些东西没有在标准中之前,我想知道为什么它应该在标准中,我没有看到匿名结构体的用途。

hgncfbus

hgncfbus5#

根据编辑、评论和这篇MSDN文章:Anonymous Structures,我大胆猜测一下--它与封装的概念不太相符。我不希望一个类的成员除了仅仅添加一个成员之外,还会扰乱我的类命名空间。此外,对匿名结构的更改可能会在未经许可的情况下影响我的类。

u4dcyp6a

u4dcyp6a6#

您的代码

union {
  struct {
    float x;
    float y;
    float z;
  };
  float v[3];
};

就像

union Foo {
   int;
   float v[3];
};

这肯定是无效的(在C99和之前)。
原因可能是为了简化解析(在C中),因为在这种情况下,您只需要检查结构体/联合体是否只有“声明符语句”,如

Type field;

也就是说,gcc和“其他编译器”支持未命名字段作为扩展。

**编辑:**匿名结构体现在在C11中得到正式支持(参见6.7.2.1/13节)。

ktecyv1j

ktecyv1j7#

https://godbolt.org/z/nT1E7e8To
这Assert并证明了在这种情况下这是定义的行为。你只需要检查你将要使用的编译器是否支持匿名结构。这是一个必须具备的特性。不使用它将是一个巨大的浪费。

#include <cstddef>

struct vec3
{
    union {
        float values[3]{};
        struct {
            float x, y, z;
        };
    };

    constexpr vec3(float x, float y, float z)
        : values { x, y, z }
    {}
};

static_assert(offsetof(vec3, values[0]) == offsetof(vec3, x), "!-_-!");
static_assert(offsetof(vec3, values[1]) == offsetof(vec3, y), "!-_-!");
static_assert(offsetof(vec3, values[2]) == offsetof(vec3, z), "!-_-!");
nxagd54h

nxagd54h8#

编辑:我给出了一个否定答案,因为我没有意识到“匿名结构体”和“未命名结构体”之间的区别。我不会删除这个答案,我会把它留在上面,但是我的回答不正确。
原始回复如下:
我在这里的任何答案中都没有看到它,我猜是因为它们大多是在“现代C++”时代之前写的,但是因为我是通过谷歌搜索“C++匿名结构”找到这里的,我就把这个答案放在这里:
我能够做到以下几点:

struct /* no typename */
{
    int i=2;
} g_some_object{};

int main()
{
    return g_some_object.i;
}

我注意到这个行为实际上在一些关于cppreference的C++20协程的例子中得到了利用,特别是在演示任务等待器时。
如果在过去的任何时候,这种行为是不允许的,那么现在情况不再是这样了--我们现在绝对可以这样做。

相关问题