一个类型 X
具有非平凡的移动构造函数,该函数 没有 声明为 noexcept(即它可能抛出异常):
#include <iostream>
#include <vector>
#include <memory>
struct X {
X()=default;
X(X&&) { std::cout << "X move constructed\n"; } // not 'noexcept'
X(const X&) { std::cout << "X copy constructed\n"; }
};
struct test {
X x;
};
int main()
{
static_assert(std::is_nothrow_move_constructible_v<X> == false);
static_assert(std::is_nothrow_move_constructible_v<test> == false);
std::vector<test> v(1);
v.push_back(test{}); // internal object re-allocation, uses copy-constructor
}
在调用vector<test>
上的push_back
时,内部重新分配会导致现有对象被复制,这是预期的行为。
X move constructed
X copy constructed
显然,第二行与内部对象重新分配相关。
现在,在test
中添加一个不相关的std::unique_ptr<int>
:
struct test {
std::unique_ptr<int> up;
X x;
};
输出结果为:
X move constructed
X move constructed
为什么这次使用移动构造函数进行内部重新分配?
在技术上,X(X&&)
仍然可能抛出异常; 那不会违反 push_back
的强异常保证吗?
编译器为 gcc 11.2.1 和/或 clang 12.0.1
emplace_back
将无法提供强异常保证。只有在必要时它才会使用非noexcept
的移动构造函数,而它更倾向于使用拷贝构造函数而不是可能抛出异常的移动构造函数,特别是为了避免破坏该保证。 - François Andrieux