多态类中没有隐式复制构造函数

No implicit copy constructor in polymorphic class?

本文关键字:复制 构造函数 多态      更新时间:2023-10-16

在 C++11 中,多态类(具有 virtual 个成员方法的类)应该/必须具有 virtual 析构函数(以便基类指针上的delete执行预期)。但是,声明析构函数显式弃用复制构造函数的隐式生成(尽管编译器可能不会广泛实现这一点),因此也会弃用默认构造函数的隐式生成。因此,要使任何多态类不被弃用,它必须具有这些成员

virtual ~polymorphic_class() = default;
polymorphic_class() = default;
polymorphic_class(polymorphic_class const&) = default;

显式定义,即使它们是微不足道的。我说的对吗?(这不是很烦人吗?这背后的逻辑是什么?有什么办法可以避免这种情况吗?

我说的对吗?

是的,根据ForEveR的帖子。

有什么办法可以避免这种情况吗?

是的。只需为所有多态类实现一个基(类似于 Java 中的类 Object,它是所有类层次结构的根,只需执行此操作一次):

struct polymorphic {
    polymorphic()                               = default;
    virtual ~polymorphic()                      = default;
    polymorphic(const polymorphic&)             = default;
    polymorphic& operator =(const polymorphic&) = default;
    // Those are not required but they don't harm and are nice for documentation
    polymorphic(polymorphic&&)                  = default;
    polymorphic& operator =(polymorphic&&)      = default;
};

然后,任何从 polymorphic 公开派生的类都将有一个隐式声明和定义(作为默认)的虚拟析构函数,除非您自己声明一个。

class my_polymorphic_class : public polymorphic {
};
static_assert(std::is_default_constructible<my_polymorphic_class>::value, "");
static_assert(std::is_copy_constructible   <my_polymorphic_class>::value, "");
static_assert(std::is_copy_assignable      <my_polymorphic_class>::value, "");
static_assert(std::is_move_constructible   <my_polymorphic_class>::value, "");
static_assert(std::is_move_assignable      <my_polymorphic_class>::value, "");

这背后的逻辑是什么?

我不能确定。以下只是猜测。

在 C++98/03 中,三法则说,如果一个类需要用户定义复制构造函数、复制赋值运算符或析构函数,那么它可能需要用户定义这三个。

遵守三法则是一种很好的做法,但这只不过是一个指导方针。标准不会强制这样做。为什么不呢?我的猜测是,人们是在标准发布后才意识到这一规则的。

C++11 引入了移动构造函数和移动赋值运算符,将三法则变成了五法则。事后看来,委员会希望执行五人规则。这个想法是:如果五个特殊函数中的任何一个是用户声明的,那么其他函数(析构函数除外)将不会隐式默认。

然而,委员会不想通过执行这条规则来破坏几乎所有C++98/03代码,然后决定只部分地这样做:

  1. 如果移动构造函数或移动赋值运算符是用户声明的,则将删除除析构函数之外的其他特殊函数。

  2. 如果五个特殊函数中的任何一个是用户声明的,则不会隐式声明移动构造函数和移动赋值运算符。

对于 C++98/03 格式正确的代码,移动构造函数和移动赋值运算符都不会用户声明,规则 1 不适用。因此,当使用符合 C++11 的编译器进行编译时,C++98/03 格式良好的代码不会因此规则而无法编译。(如果是这样,那是出于其他一些原因。

此外,根据规则 2,移动构造函数和移动赋值运算符不会隐式声明。这也不会破坏C++98/03格式良好的代码,因为他们从未期望过移动操作的声明。

OP中提到的弃用以及在ForEveR的帖子中引用的,表明未来的标准可能会强制执行五人规则。做好准备!

你是对的,将来的标准应该是这样,但现在它只是被弃用了,所以每个编译器都应该支持隐式声明的复制构造函数,当析构函数现在是虚拟的。

N3376 12.8/7

如果类定义未显式声明复制构造函数,则隐式声明一个。如果类定义声明移动构造函数或移动赋值运算符,隐式声明的复制构造函数定义为已删除;否则,它被定义为默认值 (8.4)。如果类具有用户声明的复制赋值运算符或用户声明的析构函数。

在我看来,您无法为此采取任何解决方法。