冲突的 CLANG"虚拟 dtor"和"已弃用的复制运算符"警告

Conflicting CLANG `virtual dtor` and `deprecated copy operator` warnings

本文关键字:quot 复制 运算符 警告 dtor CLANG 虚拟 冲突      更新时间:2023-10-16

在我的Qt项目中,我有一个如下所示的类。我收到以下警告(已理解(,并添加一个虚拟 dtor。

dbmappingcomponentaware.h:25: warning: CDbMappingComponentAware has virtual functions but non-virtual destructor

现在我得到

warning: definition of implicit copy assignment operator for 'CDbMappingComponentAware' is deprecated because it has a user-declared destructor

该类是一个普通的香草类,我没有声明任何运算符。我将如何解决这一冲突?

至于如何在有用户定义的析构函数时禁用隐式定义的复制构造函数生成,看起来我必须接受"弃用"警告,但我不确定。

代码片段:

//! Allows subcomponents to gain access to model component
class CDbMappingComponentAware {
public:
//! Destructor (added for 1st warning)
// virtual ~CDbMappingComponentAware() {}
//! Set the corresponding component
virtual void setMappingComponent(CDbMappingComponent *component);
...
...

--根据评论进行编辑 --

读 https://en.cppreference.com/w/cpp/language/copy_assignment 它说,

隐式定义的复制赋值运算符的生成为 已弃用(自 C++11 起(,如果 T 具有用户声明的析构函数或 用户声明的复制构造函数。

但这是为什么呢?我的意思是,为什么 dtor 会影响复制运算符?

至于 如何在有用户定义的析构函数时禁用隐式定义的复制构造函数生成,看起来我必须接受"弃用"警告,但我不确定。

您不必接受"已弃用"警告。这里有 3 个选项,实现运算符、显式默认运算符或删除运算符。

首先,此警告是触发的,因为您实际上在某处使用了copy assignment operator。甚至可能不是故意的(请参阅以下示例中的//unfortunate mistake(。除了使用运算符的警告之外,您实际上还应该得到一个注释,例如:

note: in implicit copy assignment operator for 'CDbMappingComponentAware' first required here

请考虑以下示例(运行版本(:

class foo {
public:
virtual ~foo() {}
// options
// implement
//foo& operator=(foo const&) {return *this;}
// default
//foo& operator=(foo const&) = default;
// delete
//foo& operator=(foo const&) = delete;
};
class bar : public foo {
public:
~bar() override {};
};
// stupid mistake... should be defined
// void fn(bar const&)
void fn(bar) {}

int main(int, char*[]) {
foo* f1 = new bar;
foo* f2 = new bar;
// triggers warning
*f1 = *f2;
// unfortunate mistake
bar b;
// triggers warning twice since bar inherits from foo
fn(b);
return 0;
}

在这种情况下,您确实在使用copy assignment operator,您应该实现它,或者如果它真的是普通的香草,则应默认它。请参阅下面为什么您最终不应该默认它。如果您根本不希望能够复制实例,请删除运算符/ctor。

顺便说一下,复制构造函数也是如此,而不仅仅是赋值运算符。在草案 n4727 中,对于复制构造函数的 §15.8.1/6和对于复制赋值运算符,在§15.8.2/2中对其进行了描述。

然而:

但这是否意味着如果我定义虚拟 dtor,我就无法复制对象(除非我显式编写复制赋值运算符(。

不可以,您仍然可以使用生成的副本 ctor/分配,只要它是警告。弃用意味着它将来(可能(成为一个错误(如果是C++可能在大约 40 年或更长时间内,如果有的话(。因此,如果您不希望您的代码将此警告作为错误吐出,当编译器不再支持它时,您应该立即修复它。


但这是为什么呢?我的意思是,为什么 dtor 会影响复制运算符?

这里的假设答案:

我能想到的一个目的是防止一些错误。考虑这个例子(取自为什么我的构造函数不能正常工作? ):

class Handle {
private:
string name;
X* p;
public:
Handle(string n)
:name(n), p(0) { /* acquire X called "name" and let p point to it */ }
~Handle() { delete p; /* release X called "name" */ }
};
void f(const string& hh)
{
Handle h1(hh);
Handle h2 = h1; // leads to disaster!
}

在这里,默认副本给了我们 h2.name==h1.name 和 h2.p==h1.p。这会导致灾难:当我们退出 f(( 时,h1 和 h2 的析构函数被调用,h1.p 和 h2.p 指向的对象被删除两次。

为了防止双重删除,您必须明确并且不依赖于生成/默认的复制构造函数/赋值。

你是说警告是为了执行三法则吗?

在这种特殊情况下:是的,我会这么说。

由于我不处理资源,因此三法则在这里没有真正的意义。

这将要求编译器考虑成员。对于这样一个不成熟的警告,几乎是很努力的。

个人想法:

恕我直言,我不相信这将成为C++的错误。有很多代码,依赖于自动生成的ctor/赋值。将此错误作为错误会破坏(字面意思(项目。-> 不会发生...