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

108

一些C++编译器允许使用匿名联合体和结构体,作为标准C++的扩展。这是一种语法糖,有时非常有用。

那么,阻止它成为标准的原理是什么?存在技术障碍吗?哲学上的障碍?还是只是没有足够的需求来证明其必要性?

以下是我所说的示例:

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

我的编译器能够接受这个,但是它警告说"无名结构体/共用体"是C++的非标准扩展


4
很明显你的意思有些混淆。请你提供一段只有通过编译器扩展才能编译的代码示例。 - Rob Kennedy
88
请注意,有两个概念听起来相似但是本质不同:未命名结构体匿名结构体。第一个是C++支持的这种格式:struct { int i; } a; a.i = 0;(类型没有名称)。第二个是C++ 不支持 的这种格式:struct { int i; }; i = 0;(类型没有名称,并且它溢出到周围的作用域)。然而,C++同时支持未命名和匿名联合体 - Johannes Schaub - litb
这似乎是一个非常有趣的VMMLib向量库。我认为问题在于联合体包含一个未命名的结构体,但我不确定。 - greyfade
1
顺便说一句,它是“匿名的”,而不是“未命名的”,并且联合体像litb所说的那样是被支持的。https://dev59.com/N2Yq5IYBdhLWcg3wui_F - Lightness Races in Orbit
@LightnessRacesinOrbit:“Nameless”是编译器警告的直接引用。我已经更新了问题,并附上了警告信息的文档链接。 - Adrian McCarthy
1
@AdrianMcCarthy:没问题(FSVO“没问题”;烦人的编译器很晦涩),但是“未命名”确切地说是一个不相关的标准概念。 - Lightness Races in Orbit
7个回答

60

正如其他人指出的那样,匿名联合在标准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++版本可能会添加它们。


3
C++ 不允许您将一个联合体的一个成员写入,然后从另一个成员中读取,除非这些成员是标准布局对象并共享自己的_公共初始序列_的成员,并且您正在写入/读取_它们_的成员在该公共初始序列内。那是被允许的(即已定义)。 - underscore_d
6
是的,如果类型具有共同的初始序列并符合标准布局,则可以这样做。然而,在C++中结构体永远不可能以这种方式与数组别名,因为“共同初始序列”的规则表明共同初始序列只能在结构体之间存在。数组没有被提及,所以它们不能像这样别名。 - Nicol Bolas
这是因为C支持匿名联合体,但不支持匿名结构体。请注意,您的脚注澄清了此处讨论的是C99或更早版本。C99标准中没有出现“匿名联合体”一词。使用-std=c99 -pedantic选项时,GCC在诊断信息中声称“ISO C99不支持未命名的结构体/联合体”。该标准未提及除未命名位域之外的任何未命名成员。我不确定结构声明是否属于声明,但如果是,则匿名联合体最多是未定义的6.7p2约束违规。 - user743382
啊,我明白它应该如何工作了。结构声明不是声明,因此6.7p2不适用于它们,但语法要求每个结构声明都包含一个结构声明符。唯一可以在语法上省略声明符的结构声明符是未命名的位域。在C99中,匿名联合因此在结构体外部是一个约束违规,在结构体成员中则是语法错误。 - user743382
@NicolBolas 处理公共初始序列的同时,数组是否也可以被包装在匿名结构体中呢?例如:union Point { struct { int x, y; }; struct { int v[2]; }; }; - Matt Eding
显示剩余2条评论

25

我想说,你可以通过使用 union 来简化你的 vector3 声明。

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

当然,匿名结构体曾经是MSVC的扩展。但是ISO C11现在允许它,以及gcc和苹果的llvm编译器也支持。

为什么是C11而不是C++11?我不确定,但实际上大多数(gcc++、MSVC++和苹果的C++编译器)C++编译器都支持它们。


1
你使用 union 不能做的事情只有:拥有静态数据成员或使用继承 - bobobobo
3
谢谢。我从未知道一个union可以像struct或class一样使用。 - Adrian McCarthy
我知道Sun studio在C++11之前默认不支持匿名结构体。如果你正在编写跨平台的代码,并且编译器没有升级到C++11,则不要使用匿名结构体。 - irsis
如果ISO允许这样做,为什么GCC不能正常工作?https://dev59.com/-XQOtIcB2Jgan1zngTY3 - Aaron Franke

3
我不太确定您的意思。C++规范的第9.5节,第2款:

A union of the form

union { member-specification } ;

is called an anonymous union; it defines an unnamed object of unnamed type.

您也可以像这样做:

您也可以做一些类似的事情:

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

并不总是非常有用...尽管在恶意宏定义中有时很有用。


15
-1是因为它表示定义了一个匿名结构体。请参考上面关于这个问题的评论——你正在定义一个无名结构体,而不是一个匿名结构体。 - Johannes Schaub - litb

1

联合体可以是匿名的;请参见标准,第9.5段第2句。

您认为匿名结构或类有什么用途?在猜测某些东西为什么不在标准中之前,我想先了解一下它应该存在的原因,而我并没有看到匿名结构的用途。


1

基于编辑、评论和这篇MSDN文章:匿名结构体,我猜测它与封装的概念不是很契合。我不希望一个类的成员除了添加一个成员之外还去干扰我的类命名空间。此外,对匿名结构的更改可能会未经允许地影响我的类。


1
由于匿名结构/联合体的创建方式(它是特殊的、内联的语法,除了通过宏之外无法隐藏),你不能惊讶于你正在使用的某个成员是匿名成员。因此,我认为这种推理毫无意义。实际原因是匿名联合在C++中得到支持,仅用于C兼容性。C不支持匿名结构(直到C11),因此C++也不支持。 - bames53

0

你的代码

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)中得到了官方支持。

5
从解析的角度来看,我认为 union { ... }struct { ... } 没有任何区别。前者是有效的,但后者不是。 - Johannes Schaub - litb
3
考虑到C++在一般情况下非常难以解析,我怀疑标准委员会禁止使用不带名称的结构体和联合体并不仅仅是为了简化解析。 - Adrian McCarthy
1
@Adrian,Adrian说得很好,我一直认为“太难实现”永远不会成为Bjarne和他的团队的问题。 - bobobobo
@bobobobo 实现难度影响了C++标准中的许多内容。例如,那些说“不需要诊断”的地方通常是因为在一般情况下检测所讨论的任何错误条件太困难或不可能实现。 - bames53
正确的是,C++11不支持匿名结构体,但支持匿名联合体。C99也是一样的。C11还额外支持匿名结构体。C11并没有“添加”对匿名联合体的支持,因为它们在C99中已经得到了支持。 - bames53
显示剩余3条评论

-2

编辑:我发布了一个非答案,因为我没有意识到“匿名结构体”和“无名结构体”的区别。与其删除这个答案,我会把它保留下来,但我的回答是不正确的。

以下是原始回答:


我在这里的任何答案中都没有看到它被提及,我猜因为它们大多是写于“现代C ++”时代之前,但由于我通过谷歌搜索“C ++匿名结构体”找到了这里,所以我会在这里放下这个答案:

我能够做到以下操作:

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

int main()
{
    return g_some_object.i;
}

我注意到这种行为实际上在cppreference的一些C++20协程示例中得到了利用,特别是用于演示任务等待器。

如果在过去的任何时候,这种行为是不允许的,那么现在已经不再是这样了 - 我们绝对可以这样做。


1
你正在使用一个“未命名”的结构体。这一直是合法的;它们只是没有类型名称的结构,但它们有一个属于该类型的实例。问题是关于“匿名”结构体,它们没有类型名称,也没有创建任何实例——这就是问题示例中的情况。 - Human-Compiler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接