C ++ 11虚拟析构函数和自动生成移动特殊函数 [英] C++11 virtual destructors and auto generation of move special functions

查看:134
本文介绍了C ++ 11虚拟析构函数和自动生成移动特殊函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C ++ 11中自动生成特殊移动函数(构造函数和赋值运算符)的规则指定不能声明析构函数。这个逻辑可能是,如果你需要在破坏中做一些特殊的事情,那么移动可能不安全。



然而,对于多态性中的正确析构函数调用,必须声明一个基类的析构函数为virtual(否则通过其基类的指针删除子类的实例将不会正确地链接析构函数)。



I 'm假设,那么,即使一个空的析构函数也会阻止编译器自动生成一个特殊的移动函数。如:

  class Base {
virtual〜Base(){}
};

但是,您可以默认析构函数,如:

  class Base {
virtual〜Base()= default;所以问题1:这将允许编译器自动生成特殊的移动函数吗?



但是,显式默认析构函数有一个问题。至少在GCC 4.8.2的情况下,签名隐含地改变为noexcept。如:

  class Base {
virtual〜Base()= default; //编译器更改为:
// virtual〜Base()noexcept;
}

虽然我在析构函数中没有noexcept的问题, clientcode:

  class Sub:public Base {
virtual〜Sub(); //这个声明现在是looser,因为没有noexcept
}

更重要的是:是否有一种方法允许在C ++ 11中自动生成特殊的移动函数,并允许适当的析构函数链接到子类(如上所述),所有没有断开子类(客户端)代码? p>

解决方案


  1. 不,默认的析构函数仍然被认为是用户定义的,的移动操作。还要声明移动操作 default -ed以使编译器生成它们。


  2. 在基类中声明移动操作为 default -ed。在派生类中,析构函数将不再是用户定义的(除非你明确说明),因此移动操作不会被删除。


所以我会这样做:

  class Base 
{
virtual〜Base()= default;
Base(Base&&)= default;
Base& operator =(Base&&)= default;
//可能还需要考虑复制操作,因为移动禁用它们
Base(const Base&)= default;
Base& operator =(const Base&)= default;
};

我强烈推荐这个演讲的人可能是最大的移动语义: http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014



或者,如果你可以得到你的手,你阅读项目17:了解特殊成员函数生成从Scott Meyers的优秀书 Effective Modern C ++ 。这个问题被很好地解释。



PS:我想你应该更多地考虑你的基类。大多数时候,你应该使用抽象类,所以不需要复制/移动它们的实例。



PSS:我认为默认析构函数被标记为 noexcept 在C ++ 11/14中,因此没有明确指定它不会导致任何问题:


继承构造函数和隐式声明的默认
构造函数,复制构造函数,移动构造函数,析构函数,
复制赋值运算符,移动赋值运算符都是
noexcept(true)默认情况下,除非他们需要调用一个函数
是noexcept(false),在这种情况下,这些函数是
noexcept(false)。



The rules for auto generating special move functions (constructor and assignment operator) in C++11 specify that no destructor can be declared. The logic is presumably that, if you need to do something special in destruction, that a move may not be safe.

However, for proper destructor calls in polymorphism, it is necessary to declare a base classes' destructor as virtual (otherwise deleting an instance of a sub class through a pointer of its base class will not properly chain the destructor).

I'm assuming, then, that even an empty destructor would prevent the compiler from automatically generating a special move functions. As in:

class Base {
    virtual ~Base() { }
};

You can, however, default the destructor, as in:

class Base {
    virtual ~Base() = default;
}

So question 1: Will this allow the compiler to auto generate special move functions?

There is a problem with the explicit default destructor, however. In at least the case of GCC 4.8.2, the signature is implicitly changed to noexcept. As in:

class Base {
    virtual ~Base() = default; // compiler changes to:
    // virtual ~Base() noexcept;
}

While I have no problem with noexcept in a destructor, this would break the following "client" code:

class Sub : public Base {
    virtual ~Sub(); // this declaration is now "looser" because of no noexcept
}

So question 2 is more to the point: is there a way to allow auto generation of special move functions in C++11 and allow proper destructor chaining to sub classes (as described above), all without breaking subclass ("client") code?

解决方案

  1. No, a defaulted destructor is still considered user defined, so it will prevent the generation of move operations. Also declare the move operations default-ed to make the compiler generate them.

  2. You need to only declare the move operations as default-ed in the base class. In the derived class, the destructor won't be user defined anymore (unless you explicitly say so), so the move operations won't be deleted.

So what I'd do is the following:

class Base
{
    virtual ~Base() = default;
    Base(Base&&) = default;
    Base& operator=(Base&&) = default;
    // probably need to think about copy operations also, as the move disables them
    Base(const Base&) = default;
    Base& operator=(const Base&) = default;
};

I highly recommend this talk by the person who contributed probably the most to the move semantics: http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014

Or, if you can get your hands on, you should read the Item 17: Understand special member function generation from Scott Meyers' excellent book Effective Modern C++. This issue is excellently explained.

PS: I think you should think a bit more about your base classes. Most of the time, you should use abstract classes, so there will be no need to copy/move instances of them.

PSS: I think by default destructors are marked noexcept in C++11/14, so not explicitly specifying it shouldn't cause any problems:

Inheriting constructors and the implicitly-declared default constructors, copy constructors, move constructors, destructors, copy-assignment operators, move-assignment operators are all noexcept(true) by default, unless they are required to call a function that is noexcept(false), in which case these functions are noexcept(false).

这篇关于C ++ 11虚拟析构函数和自动生成移动特殊函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆