vector::push_back 使用 std::move 进行内部重新分配
vector::push_back using std::move for internal reallocations
类型 X
有一个非平凡的移动构造函数,它是 not 声明的 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
显然,第二行与内部对象重新分配有关。
现在一个不相关的 std::unique_ptr<int>
添加到 test
:
struct test {
std::unique_ptr<int> up;
X x;
};
输出变为:
X move constructed
X move constructed
为什么这次使用移动构造函数进行内部重新分配?
从技术上讲,X(X&&)
仍然可以抛出异常;这不会违反 push_back
的强异常保证吗?
编译器是 gcc 11.2.1 and/or clang 12.0.1
根据 [vector.modifiers]/2 如果 std::vector
的元素类型不是 copy-insertable 而不是 nothrow-move-constructible,则元素类型的移动构造函数抛出异常在容器的未指定状态。
如果类型不是 nothrow-movable,std::vector
必须优先使用复制构造函数,以便可以保留异常保证,但如果不可能,则不可能给出强异常保证。
类型 X
有一个非平凡的移动构造函数,它是 not 声明的 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
显然,第二行与内部对象重新分配有关。
现在一个不相关的 std::unique_ptr<int>
添加到 test
:
struct test {
std::unique_ptr<int> up;
X x;
};
输出变为:
X move constructed
X move constructed
为什么这次使用移动构造函数进行内部重新分配?
从技术上讲,X(X&&)
仍然可以抛出异常;这不会违反 push_back
的强异常保证吗?
编译器是 gcc 11.2.1 and/or clang 12.0.1
根据 [vector.modifiers]/2 如果 std::vector
的元素类型不是 copy-insertable 而不是 nothrow-move-constructible,则元素类型的移动构造函数抛出异常在容器的未指定状态。
std::vector
必须优先使用复制构造函数,以便可以保留异常保证,但如果不可能,则不可能给出强异常保证。