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

Why does C++ disallow anonymous structs?

提问人:Adrian McCarthy 提问时间:2/13/2010 最后编辑:underscore_dAdrian McCarthy 更新时间:11/28/2022 访问量:78120

问:

一些 C++ 编译器允许匿名联合和结构作为标准 C++ 的扩展。这是一些句法糖,偶尔很有帮助。

阻止它成为标准一部分的理由是什么?是否存在技术障碍?一个哲学的?还是没有足够的必要来证明它的合理性?

以下是我所说的示例:

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

我的编译器会接受这一点,但它警告说“无名结构/联合”是C++的非标准扩展

C++ 结构 联合

评论

4赞 Rob Kennedy 2/13/2010
显然,你的意思有些混淆。您能否提供一个仅由于编译器扩展而编译的代码示例?
88赞 Johannes Schaub - litb 2/13/2010
请注意,有两个概念听起来很相似,但却截然不同:未命名结构和匿名结构。第一个是这个,C++支持:(类型没有名称)。第二个是这个,C++不支持:(该类型没有名称,它转义到周围的作用域)。但是,C++确实支持未命名和匿名联合struct { int i; } a; a.i = 0;struct { int i; }; i = 0;
0赞 greyfade 2/13/2010
这看起来像是相当有趣的 VMMLib 向量库。我相信问题是联合包含一个未命名的结构,但我不确定。
1赞 Lightness Races in Orbit 1/10/2013
FWIW 这是“anonmyous”,而不是“未命名”,正如 litb 所说,工会得到了支持。stackoverflow.com/q/14248044/560648
1赞 Lightness Races in Orbit 1/11/2013
@AdrianMcCarthy:这很好(FSVO是“好”,讨厌的编译器是神秘的),但确切地说,“未命名”是一个不相关的标准概念。

答:

3赞 Dan 2/13/2010 #1

不知道你的意思。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 };
}

并不总是很有用......尽管有时在令人讨厌的宏定义中很有用。

评论

15赞 Johannes Schaub - litb 3/27/2010
-1,因为它说它定义了一个匿名结构。请参阅上面关于该问题的评论 - 您正在定义一个未命名的结构,而不是一个匿名结构。
1赞 David Thornley 2/13/2010 #2

工会可以是匿名的;见《标准》第9.5条第2款。

你认为匿名结构或类的目的是什么?在推测为什么某些东西不在标准中之前,我想先了解一下为什么它应该如此,而且我认为匿名结构没有用处。

0赞 kennytm 2/13/2010 #3

您的代码

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赞 Johannes Schaub - litb 2/13/2010
从解析的角度来看,我认为这与.前者有效,后者无效。union { ... }struct { ... }
3赞 Adrian McCarthy 2/13/2010
鉴于 C++ 总体上难以解析是多么荒谬,我怀疑标准是否仅仅为了简化解析而不允许未命名的结构和联合。
0赞 kennytm 2/13/2010
@Adrian:我说的是C,不是C++。C++ 采用 C 的语法并对其进行扩展。可能C++的创建者认为没有必要允许未命名的结构/联合成员,这样他们就不会弄乱语法的那部分。
1赞 bobobobo 6/8/2010
@Adrian,Adrian,我一直认为“太难实现”不会成为 Bjarne 和工作人员关心的问题
0赞 bames53 10/9/2012
C 和 C++ 都支持未命名的联合,因此无效的注释不正确。union { ... };
1赞 JonM 2/13/2010 #4

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

评论

1赞 bames53 10/9/2012
由于创建匿名结构/联合的方式(它是特殊的内联语法,除非被宏隐藏),因此您不会对您正在使用的某些成员是匿名成员感到惊讶。所以我认为这种推理没有任何意义。实际原因是 C++ 支持匿名联合,仅用于 C 兼容性。C 不支持匿名结构(直到 C11),因此 C++ 也不支持。
60赞 bames53 10/9/2012 #5

正如其他人所指出的,在标准C++中允许匿名联合,但匿名结构则不允许。

这样做的原因是 C 支持匿名联合,但不支持匿名结构*,因此 C++ 支持前者以实现兼容性,但不支持后者,因为不需要兼容性。

此外,C++中的匿名结构没有多大用处。您演示的用法是,拥有一个包含三个浮点数的结构,这些浮点数可以由 、 或 、 和 引用,我相信会导致 C++ 中未定义的行为。C++ 不允许你写信给一个联合的一个成员,比如 ,然后从另一个成员那里读,比如 。尽管这样做的代码并不少见,但实际上并没有很好地定义。.v[i].x.y.z.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赞 underscore_d 6/9/2016
“C++ 不允许你写信给工会的一个成员 [...]然后从另一个成员那里读取“ - 除非所述成员是标准布局对象,并且共享其成员的公共初始序列,并且您正在所述公共初始序列中编写/读取成员。这是允许的(即定义)。
6赞 Nicol Bolas 8/22/2016
@underscore_d:是的,如果类型是具有通用初始序列的标准布局。但是,结构永远不能以这种方式与数组别名,因为 C++ 的“通用初始序列”规则规定通用初始序列只能在结构之间。没有提到数组,所以它们不能像这样别名。
0赞 underscore_d 8/22/2016
@NicolBolas 哦,哈哈 - 相信我 - 我曾多次希望数组和其他原语包含在这个津贴中!但我没有过多考虑它可能的实际限制,所以它可能并不像现在看起来那么简单。我的评论比较笼统,但可能会冒着遗漏的风险暗示我认为数组包含在其中,所以感谢您将其添加进去。
0赞 9/9/2017
“这样做的原因是 C 支持匿名联合,但不支持匿名结构”——不。您的脚注澄清了您在这里谈论的是 C99 或更早。“匿名联合”一词在 C99 标准中没有出现。GCC 在诊断(带有 -std=c99 -pedantic 选项)中声称“ISO C99 不支持未命名的结构/联合”。该标准没有提到除未命名位域之外的任何未命名成员。我不完全确定结构声明是否是声明,但如果它们是,匿名联合是违反 6.7p2 的约束,充其量是未定义的。
0赞 9/9/2017
啊,弄清楚它应该如何工作。struct-declarations 不是声明,所以 6.7p2 不适用于它们,但语法要求每个 struct-declaration 都包含一个 struct-declarator。语法上唯一允许省略声明符的结构声明符是未命名的位域。因此,在 C99 中,匿名联合是结构之外的约束冲突,也是结构成员的语法错误。
25赞 bobobobo 10/16/2012 #6

我会说,你可以通过使用vector3union

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

当然,匿名结构 MSVC 的扩展。但是 ISO C11 现在允许它,gcc 允许它,Apple 的 llvm 编译器也是如此。

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

评论

1赞 bobobobo 10/16/2012
唯一不能使用联合执行的操作是具有静态数据成员或使用继承
3赞 Adrian McCarthy 10/18/2012
谢谢。我从来没有新过一个联合可以像结构体或类一样使用。
0赞 irsis 5/12/2015
我知道默认情况下,Sun Studio 在 C++11 之前并不支持匿名结构。如果您正在编写跨平台代码,并且编译器没有升级到 C+11,那么请不要使用匿名结构。
0赞 Aaron Franke 5/15/2022
如果 ISO 允许这样做,为什么它不适用于 GCC?stackoverflow.com/questions/72137275/......
-2赞 Teeeeeeeeeeeeeeeeeeeeeeeeeeeej 7/2/2022 #7

编辑:我提出了一个非答案,因为我没有意识到“匿名结构”和“未命名结构”之间的区别。我不会删除这个答案,而是把它留下来,但我在这里的回答是不正确的。

原文如下:


我在这里的任何答案中都没有提到它,我想因为它们大多是在“现代C++”时代之前写的,但是由于我通过谷歌搜索“C++匿名结构”找到了我的方式,我将把这个答案放在这里:

我能够做到以下几点:

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

int main()
{
    return g_some_object.i;
}

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

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

评论

1赞 Human-Compiler 7/2/2022
您正在使用未命名的 .这些一直是合法的;它们只是一个没有类型名称的结构,但它们有一个属于该类型的实例。这个问题是关于匿名的,它没有类型名称也没有创建实例 - 这就是问题示例中的内容。structstruct