为什么std :: vector使用移动构造函数,尽管声明为noexcept(false)

时间:2014-10-06 20:11:59

标签: c++ c++11 vector move-semantics noexcept

无论我在互联网上阅读什么,强烈建议如果我希望我的班级能够与std::vector一起使用(即std::vector使用我班级的移动语义)我应该去看看构造函数为'noexcept'(或noexcept(true))。

为什么std::vector使用它,即使我将其标记为noexcept(false)作为实验?

#include <iostream>
#include <vector>
using std::cout;

struct T
{
    T() { cout <<"T()\n"; }

    T(const T&) { cout <<"T(const T&)\n"; }

    T& operator= (const T&)
    { cout <<"T& operator= (const T&)\n"; return *this; }

    ~T() { cout << "~T()\n"; }

    T& operator=(T&&) noexcept(false)
    { cout <<"T& operator=(T&&)\n"; return *this; }

    T(T&&) noexcept(false)
    { cout << "T(T&&)\n"; }
};

int main()
{
    std::vector<T> t_vec;
    t_vec.push_back(T());
}

输出:

T()
T(T&&)
~T()
~T()

为什么? 我做错了什么?

在gcc 4.8.2上编译,CXX_FLAGS设置为:

--std=c++11 -O0 -fno-elide-constructors

2 个答案:

答案 0 :(得分:12)

你没有做错任何事。

你错误地认为push_back必须避免投掷移动 - 它不会,至少不是为了构建新元素。

唯一必须避免投掷移动控制器/移动分配的地方是重新分配矢量,以避免移动一半元素,其余元素移动到其原始位置。

该功能具有强大的异常安全保障:

操作成功,或者失败并且没有任何改变。

答案 1 :(得分:4)

如果vector::push_back需要重新分配其存储空间,则首先分配新内存,然后移动将新元素构造到最后位置。如果抛出新内存被释放,并且没有任何更改,即使移动构造函数可以抛出,也会获得强大的异常安全保证。

如果它没有抛出,现有元素将从原始存储转移到新存储, here 是移动构造函数的noexcept规范重要的地方。如果移动可能会抛出并且类型为CopyConstructible,则将复制现有元素而不是移动。

但是在你的测试中,你只关注新元素如何插入到向量中,并且在该步骤中使用抛出构造函数总是可以的。