为什么具有虚函数的C++类需要具有非平凡的复制构造函数?

Гри*_*ков 13 c++ language-lawyer c++11

根据C++标准,具有虚函数的类不能具有普通的复制构造函数:

如果不是用户提供的,则类X的复制/移动构造函数是微不足道的

- 类X没有虚函数(10.3),没有虚基类(10.1),和

- 选择复制/移动每个直接基类子对象的构造函数是微不足道的,并且

- 对于类类型(或其数组)的X的每个非静态数据成员,选择复制/移动该成员的构造函数是微不足道的;

否则复制/移动构造函数是非平凡的.

现在,想象一个类层次结构,它满足除"无虚函数"条件之外的所有上述条件:

struct I
{
    virtual void f() = 0;
};

struct D final : I
{
   void f() override 
   {
   }
};
Run Code Online (Sandbox Code Playgroud)

从实现的角度来看,这些类只包含指向虚拟调度表的指针.并且基类没有任何用户声明的构造函数,并且派生类是final,因此提到的指针总是可以具有常量值.鉴于这一切,复制构造函数可以是微不足道的,但标准明确禁止对其进行处理.

另一方面,满足了对诸如可破坏的类别进行处理的条件.该类不声明虚拟析构函数并使用隐式定义的析构函数.

要求简单的析构函数看起来像强制优化 - 在这种情况下,隐式定义的析构函数不应该恢复指向虚拟表的指针.

但是在我看来,这样的优化是中途的; 具有虚函数的类仍然无法进行memcopied,即使它们可以被轻易破坏.

问题:

  1. 我是否有任何理由从实现角度考虑为什么这些类应该有非平凡的复制构造函数?

  2. 是否有任何理由不能在标准中放宽对复制构造函数的琐碎限制?

Rev*_*lot 8

我是否有任何理由从实现角度考虑为什么这些类应该有非平凡的复制构造函数?

有一个非常明显的一点:复制构造函数I并非无足轻重.它不是最终的,所以可以有其他派生类.所以它必须是非平凡的并且在之后正确设置虚拟表指针memcpy,因为可能存在依赖于它的派生类.

是否有任何理由不能在标准中放宽对复制构造函数的琐碎限制?

1)构造函数的琐碎部分根本没有修改,包含了final关键字.

2)人们认为的关键字时delete,finaloverrride应有助于避免最常见的错误,并阐明程序员的意图,不改变程序的行为.

3)它使语言变得复杂: 构造函数是微不足道的,除非你有虚函数,然后它是不平凡的,除非你的类是最终的,然后它是微不足道的,除非别的,否则它不是,除非......

4)没有人认为值得为正式文件撰写,证明这一增加对委员会的有用性并将这种变化推向语言.


Cas*_*sey 6

多态类的副本和移动不可能是微不足道的,因为它会破坏切片副本和移动,从而复制对象动态类型的基本类型.例如:

struct Base { virtual int f() { return 0; } };
struct D1 : Base { int f() override { return 1; } };
struct D2 : Base { int f() override { return 2; } };

int main() {
  D1 d1;
  D2 d2;
  Base b = d1; // if this copy constructor were trivial, b would have D1's vtable
  b.f();       // ...and this call would return 1 instead of 0.
  b = d2;      // Ditto: b would have D2's vtable
  b.f();       // ...and this call would return 2 instead of 0.
}
Run Code Online (Sandbox Code Playgroud)