类型X
有一个非平凡的移动构造函数,它不是声明的noexcept(即它可以抛出(:
#include <iostream>
#include <vector>
#include <memory>
struct X {
X()=default;
X(X&&) { std::cout << "X move constructedn"; } // not 'noexcept'
X(const X&) { std::cout << "X copy constructedn"; }
};
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
显然,第二行与内部对象重新分配有关。
现在,一个不相关的std::unique_ptr<int>
被添加到test
:
struct test {
std::unique_ptr<int> up;
X x;
};
输出变为:
X move constructed
X move constructed
为什么这次内部重新分配使用move构造函数
从技术上讲,X(X&&)
仍然可以抛出异常;这不会违反push_back
的强异常保证吗?
编译器是gcc 11.2.1和/或clang 12.0.1
根据[vector.modifiers]/2,如果std::vector
的元素类型不可复制插入,也不可nothrow移动构造,则该元素类型的移动构造函数抛出的异常会导致容器处于未指定状态。
如果类型不是另一行可移动的,std::vector
必须更喜欢复制构造函数,以便可以保留异常保证,但如果这不可能,则不可能提供强异常保证。