C ++为什么在Move Constructors和Move Assignment Operators的上下文中需要noexcept来启用优化?

时间:2015-08-26 10:37:37

标签: c++11 move-semantics noexcept

考虑以下类,使用移动构造函数和移动赋值运算符:

class my_class
{

    protected:

    double *my_data;
    uint64_t my_data_length;
}

my_class(my_class&& other) noexcept : my_data_length{other.my_data_length}, my_data{other.my_data}
{
    // Steal the data
    other.my_data = nullptr;
    other.my_data_length = 0;
}

const my_class& operator=(my_class&& other) noexcept
{
    // Steal the data
    std::swap(my_data_length, other.my_data_length);
    std::swap(my_data, other.my_data);

    return *this;
}

这里noexcept的目的是什么?我知道编译器遇到以下函数不应抛出任何异常, but how does this enable compiler optimizations?

3 个答案:

答案 0 :(得分:4)

noexcept

详细解释了push_back(t)对移动构造函数和赋值运算符的特殊重要性

基本上,它不允许传统意义上的“优化”允许编译器生成更好的代码。相反,它允许其他类型(例如库中的容器)在检测到移动元素类型永远不会抛出时采用不同的代码路径。这可以使得一个替代代码路径如果它们可以抛出则不安全(例如,因为它会阻止容器遇到https://vimeo.com/channels/ndc2014/97337253)。

例如,当您对向量执行size() == capacity()时,如果向量已满(noexcept),则需要分配新的内存块并将所有现有元素复制到新内存中。如果复制任何元素会抛出异常,那么库只会破坏它在新存储中创建的所有元素并释放新内存,而原始向量将保持不变(从而满足强大的异常安全保证)。将现有元素移动到新存储会更快,但如果移动可能会抛出,那么任何已经移动的元素都会被修改,并且无法满足强大的保证,因此库只会尝试移动它们它知道不能抛出,它只能知道它们是const

答案 1 :(得分:1)

使用noexcept的IMHO不会自行启用任何编译器优化。 STL有一些特点:

std::is_nothrow_move_constructible
std::is_nothrow_move_assignable

vector之类的STL包含器使用这些特性来测试类型T并使用移动构造函数和赋值而不是复制构造函数和赋值。

为什么STL使用这些特征而不是:

std::is_move_constructible
std::is_move_assignable

答案:提供强有力的例外保证。

答案 2 :(得分:0)

首先,我要说的是,在移动构造函数或移动赋值中,什么都不应该抛出,似乎没有必要这样做。必须在构造函数/赋值运算符中完成的唯一事情是处理已分配的内存和指向它们的指针。通常你不应该调用任何其他可以抛出的方法,你自己在构造函数/操作符中移动也不需要这样做。但另一方面,调试消息的简单输出违反了这一规则。

优化可以通过一些不同的方式完成。由编译器自动执行,也由使用构造函数和赋值运算符的代码的不同实现自动执行。看看STL,如果你使用的是异常,那么代码的某些特殊化是不同的,这些异常是通过类型特征实现的。

编译器本身可以更好地进行优化,同时保证任何代码都不会抛出。编译器通过您的代码有一个保证的调用树,可以更好地内联,计算编译时间或者如此。可以完成的最小优化是不存储处理throw条件所需的关于实际堆栈帧的所有信息,比如堆栈上的释放变量和其他东西。

此处还有一个问题:noexcept, stack unwinding and performance

也许你的问题与此重复?

我在此处找到一个与此相关的有用问题:Are move constructors required to be noexcept? 这讨论了投掷移动操作的必要性。

  

noexcept的目的是什么?

至少可以节省一些程序空间,这不仅与移动操作相关,而且与所有功能相关。如果您的类与STL容器或算法一起使用,它可以处理不同的,如果您的STL实现使用这些信息,可以导致更好的优化。也许编译器能够获得更好的一般优化,因为如果所有其他事情都是编译时间常量的话,它就会有一个已知的调用树。