无论我在互联网上阅读什么,强烈建议如果我希望我的班级能够与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
答案 0 :(得分:12)
你没有做错任何事。
你错误地认为push_back
必须避免投掷移动 - 它不会,至少不是为了构建新元素。
唯一必须避免投掷移动控制器/移动分配的地方是重新分配矢量,以避免移动一半元素,其余元素移动到其原始位置。
该功能具有强大的异常安全保障:
操作成功,或者失败并且没有任何改变。
答案 1 :(得分:4)
如果vector::push_back
需要重新分配其存储空间,则首先分配新内存,然后移动将新元素构造到最后位置。如果抛出新内存被释放,并且没有任何更改,即使移动构造函数可以抛出,也会获得强大的异常安全保证。
如果它没有抛出,现有元素将从原始存储转移到新存储, here 是移动构造函数的noexcept
规范重要的地方。如果移动可能会抛出并且类型为CopyConstructible,则将复制现有元素而不是移动。
但是在你的测试中,你只关注新元素如何插入到向量中,并且在该步骤中使用抛出构造函数总是可以的。