使用std::move进行内部重新分配时的vector::push_back

3

一个类型 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


1
如果一个元素的移动构造函数抛出异常,emplace_back 将无法提供强异常保证。只有在必要时它才会使用非 noexcept 的移动构造函数,而它更倾向于使用拷贝构造函数而不是可能抛出异常的移动构造函数,特别是为了避免破坏该保证。 - François Andrieux
1个回答

2
根据[vector.modifiers]/2,如果std::vector的元素类型既不支持拷贝插入也不支持无抛移动构造,则来自元素类型的移动构造函数的异常会导致容器处于未指定状态。
如果类型不支持无抛移动构造,std::vector必须优先选择拷贝构造函数,以便保留异常保证,但如果这不可能,则无法提供强异常保证。

强大的保证算什么啊.. 在我看来,它应该拒绝这段代码,但我不在委员会里。感谢您的回答。 - curiousguy12

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接