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 必须优先使用复制构造函数,以便可以保留异常保证,但如果不可能,则不可能给出强异常保证。