在C ++中,聚合(取自语言规范的8.5.1p1)
数组或类(第9条),没有用户提供的构造函数(12.1),没有私有或 受保护的非静态数据成员(第11条),没有基类(第10条),没有虚函数(10.3)。
因此,#1
不是聚合,但#2
是聚合。为什么#1
不是聚合呢?
struct A { virtual void bark() { } int a; }; // #1
struct B { A b; }; // #2
答案 0 :(得分:6)
为什么#1不是聚合呢?
因为聚合的标准定义表明它不是。定义说如果要将类视为聚合类型,则该类不能具有虚函数。而已。那是“队长显而易见”的答案。
既然你引用了标准定义(并且读了它,我猜),你已经知道了,所以我必须假设你所问的是具有虚函数的类是否有任何根本原因不能是聚合类型
显然,具有虚函数的类必须将其虚拟表指针(或其他机制)初始化为编译器生成的构造函数的一部分。并且聚合实际提供的唯一东西(除了作为POD等的基本定义之外)是能够对其数据成员进行大括号初始化。我没有看到为什么编译器在执行虚拟表指针的初始化时也不能简单地允许大括号初始化语法的原因。
就B
而言,它可以是聚合,因为只要有一种方法可以从初始化中提供(或未提供)的任何内容构造数据成员,就可以进行大括号初始化。 list,即它需要每个数据成员都有一些(编译器生成的或非生成的)默认,复制或移动构造函数。请记住,聚合的定义是浅而不是像
聚合的定义显然很容易让人联想到C结构限制,结构的大括号初始化显然也是C的一个特性。我认为,由于历史原因,聚合的定义是这样构建的。反映C结构,而不是出于编译器可能或不可能的原因。
我当然认为没有充分理由限制聚合没有虚函数。也许应该向标准委员会提出建议,以取消这一限制(因为它不会破坏任何现有的代码)。特别是现在,使用统一初始化语法,聚合只不过是编译器可以生成构造函数的类,其中所有数据成员都是参数(默认值是默认构造的对象)。聚合的唯一其他目的是将一些适用于POD类的限制(平凡,标准布局等),其中没有虚函数的限制是合理的(AFAIK),但这只是一个将限制转移到POD上的问题。
答案 1 :(得分:1)
当他们将这种限制添加到聚合的定义时,我无法确定委员会的想法。我怀疑它是因为他们认为要求编译器也必须为虚函数调用(vtable或其他)设置实例,这与简单地初始化值是完全不同的,因此增加了限制以简化对语言实现者。