简体   繁体   English

警告:不推荐使用隐式复制构造函数的定义

[英]Warning: definition of implicit copy constructor is deprecated

I have a warning in my C++11 code that I would like to fix correctly but I don't really know how.我的 C++11 代码中有一条警告,我想正确修复,但我真的不知道如何修复。 I have created my own exception class that is derived from std::runtime_error :我创建了我自己的异常 class,它源自std::runtime_error

class MyError : public std::runtime_error
{
public:
    MyError(const std::string& str, const std::string& message)
      : std::runtime_error(message),
        str_(str)
    { }

    virtual ~MyError()
    { }

    std::string getStr() const
    {
        return str_;
    }

  private:
      std::string str_;
};

When I compile that code with clang-cl using /Wall I get the following warning:当我使用/Wall使用 clang-cl 编译该代码时,我收到以下警告:

warning: definition of implicit copy constructor for 'MyError' is deprecated 
         because it has a user-declared destructor [-Wdeprecated]

So because I have defined a destructor in MyError no copy constructor will be generated for MyError .所以因为我在MyError中定义了一个析构函数,所以不会为MyError生成复制构造函数。 I don't fully understand if this will cause any issues...我不完全明白这是否会导致任何问题......

Now I could get rid of that warning by simply removing the virtual destructor but I always thought that derived classes should have virtual destructors if the base class (in this case std::runtime_error ) has a virtual destructor.现在我可以通过简单地删除虚拟析构函数来消除该警告,但我一直认为如果基类 class(在本例中为std::runtime_error )具有虚拟析构函数,则派生类应该具有虚拟析构函数。

Hence I guess it is better not to remove the virtual destructor but to define the copy constructor.因此我想最好不要删除虚拟析构函数,而是定义复制构造函数。 But if I need to define the copy constructor maybe I should also define the copy assignment operator and the move constructor and the move assignment operator.但是如果我需要定义复制构造函数,也许我还应该定义复制赋值运算符、移动构造函数和移动赋值运算符。 But this seems overkill for my simple exception class??但这对于我的简单异常 class 来说似乎有些矫枉过正?

Any ideas how to best fix this issue?任何想法如何最好地解决这个问题?

You do not need to explicitly declare the destructor in a derived class:您不需要在派生类中显式声明析构函数:

§ 15.4 Destructors [class.dtor] (emphasis mine) § 15.4 析构函数 [class.dtor] (强调我的)

A destructor can be declared virtual (13.3) or pure virtual (13.4);析构函数可以声明为虚拟(13.3)或纯虚拟(13.4); if any objects of that class or any derived class are created in the program, the destructor shall be defined.如果在程序中创建了该类或任何派生类的任何对象,则应定义析构函数。 If a class has a base class with a virtual destructor, its destructor (whether user- or implicitly-declared) is virtual .如果一个类有一个带有虚拟析构函数的基类,它的析构函数(无论是用户声明的还是隐式声明的)是 virtual

In fact, it might even be detrimental to performance in some cases, as explicitly declaring a destructor will prevent the implicit generation of a move constructor and move assignment operator.事实上,在某些情况下它甚至可能对性能有害,因为显式声明析构函数将阻止隐式生成移动构造函数和移动赋值运算符。

Unless you need to do something in your destructor, the best course of action would be to just omit an explicit declaration of a destructor.除非您需要在析构函数中执行某些操作,否则最好的做法是省略对析构函数的显式声明。

If you do need a custom destructor, and are certain that the default copy ctor, copy assignment operator, move ctor and move assignment operator would do the correct thing for you, it is best to explicitly default them like so:如果您确实需要自定义析构函数,并且确定默认的复制构造函数、复制赋值运算符、移动构造函数和移动赋值运算符会为您做正确的事情,最好像这样明确地默认它们:

MyError(const MyError&) = default;
MyError(MyError&&) = default;
MyError& operator=(const MyError&) = default;
MyError& operator=(MyError&&) = default;

Some reasoning on why you're seeing the error, because this used to be perfeclty valid code in C++98:关于为什么会看到错误的一些推理,因为这曾经是 C++98 中的完美有效代码:

As of C++11, implicit generation of the copy constructor is declared as deprecated.从 C++11 开始,复制构造函数的隐式生成被声明为已弃用。

§ D.2 Implicit declaration of copy functions [depr.impldec] § D.2 复制函数的隐式声明 [depr.impldec]

The implicit definition of a copy constructor as defaulted is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则不推荐使用默认的复制构造函数的隐式定义。 The implicit definition of a copy assignment operator as defaulted is deprecated if the class has a user-declared copy constructor or a user-declared destructor (15.4, 15.8).如果类具有用户声明的复制构造函数或用户声明的析构函数 (15.4, 15.8),则不推荐使用默认的复制赋值运算符的隐式定义。 In a future revision of this International Standard, these implicit definitions could become deleted (11.4).在本国际标准的未来修订版中,这些隐含的定义可能会被删除(11.4)。

The rationale behind this text is the well-known Rule of three.这段文字背后的基本原理是众所周知的三法则。

All quotes below are sourced from cppreference.com: https://en.cppreference.com/w/cpp/language/rule_of_three :所有报价低于从cppreference.com来源https://en.cppreference.com/w/cpp/language/rule_of_three

Rule of Three三规则

If a class requires a user-defined destructor, a user-defined copy constructor, or a user-defined copy assignment operator, it almost certainly requires all three.如果一个类需要一个用户定义的析构函数、一个用户定义的复制构造函数或一个用户定义的复制赋值运算符,那么它几乎肯定需要所有三个。

The reason why this rule of thumb exists is because the default generated dtor, copy ctor and assignment operator for handling different types of resources (most notably pointers to memory, but also others, like file descriptors and network sockets to name just a couple) rarely do the correct behaviour.之所以存在这条经验法则,是因为默认生成的 dtor、copy ctor 和赋值运算符用于处理不同类型的资源(最显着的是指向内存的指针,但也有其他的,例如文件描述符和网络套接字,仅举几例)做正确的行为。 If the programmer thought that he needed special handling for the closing of a file handle in the class destructor, he most surely wants to define how this class should be copied or moved.如果程序员认为他需要特殊处理来关闭类析构函数中的文件句柄,那么他肯定想定义应该如何复制或移动这个类。

For completeness, below are the often related Rule of 5, and the somewhat disputed Rule of Zero为了完整起见,下面是经常相关的 5 规则,以及有点争议的零规则

Rule of Five五条法则

Because the presence of a user-defined destructor, copy-constructor, or copy-assignment operator prevents implicit definition of the move constructor and the move assignment operator, any class for which move semantics are desirable, has to declare all five special member functions:因为用户定义的析构函数、复制构造函数或复制赋值运算符的存在阻止了移动构造函数和移动赋值运算符的隐式定义,任何需要移动语义的类都必须声明所有五个特殊成员函数:

Rule of Zero零规则

Classes that have custom destructors, copy/move constructors or copy/move assignment operators should deal exclusively with ownership (which follows from the Single Responsibility Principle).具有自定义析构函数、复制/移动构造函数或复制/移动赋值运算符的类应专门处理所有权(遵循单一职责原则)。 Other classes should not have custom destructors, copy/move constructors or copy/move assignment operators.其他类不应具有自定义析构函数、复制/移动构造函数或复制/移动赋值运算符。

Now I could get rid of that warning by simply removing the virtual destructor but I always thought that derived classes should have virtual destructors if the base class (in this case std::runtime_error) has a virtual destructor.现在我可以通过简单地删除虚拟析构函数来摆脱这个警告,但我一直认为如果基类(在这种情况下 std::runtime_error)有一个虚拟析构函数,派生类应该有虚拟析构函数。

You thought wrong.你想错了。 Derived classes will always have virtual destructor if you define one in base, no matter if you create it explicitly or not.如果您在基类中定义一个,派生类将始终具有虚拟析构函数,无论您是否显式创建它。 So removing destructor would be simplest solution.所以删除析构函数将是最简单的解决方案。 As you can see in documentation for std::runtime_exception it does not provide it's own destructor either and it is compiler generated because base class std::exception does have virtual dtor.正如您在std::runtime_exception文档中所见,它也不提供自己的析构函数,并且它是编译器生成的,因为基类std::exception确实具有虚拟 dtor。

But in case you do need destructor you can explicitly add compiler generated copy ctor:但如果您确实需要析构函数,您可以显式添加编译器生成的复制构造函数:

MyError( const MyError & ) = default;

or prohibit it making class not copyable:或禁止它使类不可复制:

MyError( const MyError & ) = delete;

the same for assignment operator.赋值运算符也是如此。

Note: the same happens for much different code but I'm writing it here in case someone gets the same warning.注意:很多不同的代码也会发生同样的情况,但我在这里写它以防有人收到相同的警告。

There was a bug in GCC versions 6.4 - 9.0 where using declarations for base_type's operator= and base_type's ctor in types inherited from base_type which was a class template did not actually create copy/move ctor/operators (ending in very unexpected compiler errors that an object can not be copied/moved). GCC 版本 6.4 - 9.0 中存在一个错误,其中在从 base_type 继承的类型使用 base_type 的 operator= 和 base_type ctor 的声明实际上并未创建复制/移动 ctor/operators (以非常意外的编译器错误结束,对象不能复制/移动)。

Since GCC 9.0, the bug is fixed but it creates this warning instead.从 GCC 9.0 开始,该错误已修复,但它会创建此警告。 The warning is wrong and should not appear (the using explicitly declares constructors/operators).警告是错误的,不应出现( using 显式声明构造函数/运算符)。

This will make your code compile (but not work) in clang 13:这将使您的代码在 clang 13 中编译(但工作):

   MyError(const MyError&) {};
   MyError(MyError&&) {};
   MyError& operator=(const MyError&) {};
   MyError& operator=(MyError&&) {};

You will need to fill in the appropriate code for the copy constructor,您需要为复制构造函数填写适当的代码,
but also be aware that you don't need all 4 functions , only the ones that are being called.但也请注意,您不需要所有 4 个函数,只需要被调用的函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM