C++的聚合体没有虚函数?

9
在C ++中,聚合体(aggregate)是指(来自语言规范的8.5.1p1节)无用户提供的构造函数(12.1),无非静态数据成员的私有或保护访问控制(第11节),无基类(第10节)和无虚函数(10.3)的数组或类(第9节)。因此,#1不是一个聚合体,但#2是一个聚合体。为什么#1也不是聚合体呢?
struct A { virtual void bark() { } int a; }; // #1
struct B { A b; }; // #2

3
"[..] and no virtual functions (10.3)" 翻译为:"[...] 没有虚函数(10.3)"。 - MrDuk
@MrDuk 是的,我可以确认这是我引用文本的最后部分。或者你想问一个不同的问题吗? - Johannes Schaub - litb
vtbl,也许?顺便说一句,我不确定struct是否符合“数组或类”的资格。 - Frédéric Hamidi
2
@FrédéricHamidi 它使用class来表示由structclassunion声明的 - Johannes Schaub - litb
B::bvtbl 指针也需要设置,所以我不确定为什么 vtbls 会排除 A 但不排除 B - Johannes Schaub - litb
显示剩余10条评论
2个回答

9
为什么#1不是聚合体?
因为标准定义表示它不是。该定义指出,如果类被视为聚合类型,则其不能具有虚函数。这就是“显而易见”的答案。
由于您引用了标准定义(并阅读了它,我假设),所以我必须假设您问的是一个类具有虚函数时为什么不能成为聚合类型的任何根本原因。
显然,具有虚函数的类必须在编译器生成的构造函数的一部分中初始化其虚表指针(或其他机制)。聚合提供的唯一附加功能(除了作为POD的基本定义之外)就是能够对其数据成员进行花括号初始化。我没有看到任何理由为什么编译器不能在初始化虚表指针的同时允许大括号初始化语法。
至于B,它可以是聚合体,因为只要有一种方法从提供的内容(或未提供的内容)中构造数据成员,即每个数据成员都需要具有一些(编译器生成的或非编译器生成的)默认、复制或移动构造函数,那么就可以使用大括号初始化。请记住,聚合的定义是“浅层”的,与POD定义(平凡的、std-layout)相反,这意味着只有顶层类具有这些限制,而不是其子对象。
聚合的定义显然非常类似于C结构体的限制,而结构体的大括号初始化显然也是从C继承过来的功能。我相信,出于历史原因,聚合的定义被构建为反映C结构体,而不是基于编译器能否执行的原因。
我肯定认为没有理由限制聚合体没有虚函数。也许应该向标准委员会提出一个提案,以删除此限制(因为它不会破坏任何现有代码)。特别是现在,统一的初始化语法使聚合体仅仅是编译器可以生成带有所有数据成员作为参数的构造函数的类(默认值为默认构造对象)。聚合的唯一其他目的是将适用于POD类(平凡的、标准布局等)的一些限制归为一堆,其中不允许具有虚函数的限制是合理的(据我所知),但这只是将该限制转移到POD中的问题。

1
很明显,一个有虚函数的类必须在编译器生成的构造函数中初始化它的虚表指针。我想强调的是,C++标准并未规定使用虚表和虚表指针。这确实是一种非常常见的virtual分发机制的实现方式,并且因此对于所有涉及virtual处理的决策都有影响,但它只是一个实现细节。 - Matthieu M.
@MatthieuM。当然,这是正确的。但是,无论实现细节如何,逻辑都是相同的。如果编译器可以为任何类(包括具有虚函数的类)生成默认构造函数(或具有内部类成员初始化器的默认构造函数),那么它应该能够生成一个构造函数以与聚合体的大括号初始化语法一起使用。我认为虚函数限制在标准中被错误地放置,它应该仅用于POD定义,而不是用于聚合体。 - Mikael Persson
1
POD-ness在C++03(C++03 [class]/4)中要求具有聚合性,但这种情况已不再适用。POD被定义为递归的trivialstandard-layout(C++11 [class]/10),两者都不需要成为聚合体。一个类/联合体可能既是POD又是聚合体,但并非必需。从聚合体中移除虚函数的限制将不需要对POD定义进行任何更改。 - Casey
@Casey,我没看出与POD有什么关系。他们本可以只禁止POD使用虚函数,而让聚合体保持强大。 - Johannes Schaub - litb
@JohannesSchaub-litb 回答中的最后一句话是:“聚合体的唯一其他目的是将适用于POD类(平凡,标准布局等)的一些限制合并在一起,其中不具有虚函数的限制是合理的(据我所知),但这只是将该限制移至POD。”仅在C++03的情况下才有意义,当时论点的前提是正确的。自C++11以来,这种说法已经不再正确。 - Casey
@Casey 感谢您指出聚合体不再影响POD定义(平凡和std-layout),我只是假设它们仍然会。我刚刚在标准中搜索了“聚合体”一词,除了零星地提到一些地方(例如,“字面类型”),聚合体的唯一真正相关性似乎就是其特殊的初始化语法,就像我说的那样,并不能证明虚函数的限制。因此,看起来更多的是出于历史原因,没有人费心为C++11/14进行修订。 - Mikael Persson

1
我无法确定委员会在将该限制添加到聚合物定义中时想到了什么。我猜想他们认为,要求编译器还必须为虚函数调用设置一个实例(vtable或其他)与仅初始化值有足够的不同之处,因此添加了该限制以简化语言实现者的要求。

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