【问题标题】:Conflicting CLANG `virtual dtor` and `deprecated copy operator` warnings冲突的 CLANG `virtual dtor` 和 `deprecated copy operator` 警告
【发布时间】:2018-08-22 16:36:38
【问题描述】:

在我的 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

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

截至How to disable implicitly-defined copy constructor generation when there is user defined destructor,看来我必须接受“已弃用”警告,但我不确定。

代码sn-p:

//! 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 它说,

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

但这是为什么呢?我的意思是,为什么 dtor 会影响复制操作员?

【问题讨论】:

  • 你可以明确地声明它们= default(或= delete)来关闭编译器。顺便说一句,无论编译器警告如何,显式通常都是一件好事。
  • 但这并不意味着如果我定义了一个虚拟 dtor 就不能复制一个对象(除非我明确地编写了一个复制赋值运算符)。为什么这有意义?
  • 你是说警告是为了强制执行三规则吗?由于我不处理资源,因此三规则在这里没有真正意义。我记得这条规则是指导方针,而不是必须的。在那种情况下,仍然不能真正理解deprecated 的警告意味着什么。还想知道为什么这是自 C++11 以来的新功能。但很好的提示!

标签: c++ qt clang


【解决方案1】:

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

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

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

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

考虑以下示例 (running version):

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,你应该实现它,或者默认如果它真的是plain vanilla时间>。请参阅下面为什么您最终不应该默认它。如果您根本不想复制实例,请删除 operator/ctor。

顺便说一句,复制构造函数也是一样的,不仅仅是赋值运算符。 §15.8.1/6 中描述了复制构造函数,§15.8.2/2 中描述了草案n4727 中的复制赋值运算符。

但是:

但这是否意味着如果我定义了一个虚拟 dtor,我就不能复制一个对象(除非我明确地编写了一个复制赋值运算符)。

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


但这是为什么呢?我的意思是,为什么 dtor 会影响复制操作员?

这里的假设答案:

我能想到的一个目的是防止一些错误。考虑这个例子(取自Why doesn’t my constructor work right?):

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/assignment 有很多代码。将其作为错误会破坏(字面上)数以千计的项目。 -> 不会发生...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-28
    • 1970-01-01
    • 2021-06-20
    • 2016-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-03
    相关资源
    最近更新 更多