bad_alloc通过右值和对象有效性插入对象时

bad_alloc upon inserting object by rvalue and object validity

本文关键字:对象 有效性 插入 alloc bad      更新时间:2023-10-16

考虑以下代码(假设 SomeClass 具有移动构造函数和指针等字段,这些字段在对象与另一个对象交换其内容后将失效(:

SomeClass data;
std::list queue;
try { queue.push_back(std::move(data)); }
catch(std::bad_alloc)
{
    data.doThingsUsingObjectData(); //Can I do this here?
    return;
}
data.doThingsUsingObjectData(); //Definitelly can't do this here - object was invalidated

关键是 - 如果在使用 std::move(( 将对象添加到 STL 时抛出bad_alloc,这是否意味着我试图通过右值添加到 STL 的对象实际上仍然有效,我可以访问它而不必担心未定义的行为?

std::list将其

元素存储在节点中。当您添加一个元素时,将分配一个新节点,然后将该元素存储在节点中(通过调用 std::allocator_traits<A>::construct ,这通常会执行放置 new,以在节点内就地构造对象(,最后通过更新新节点和相邻节点的指针将其链接到列表中,将节点添加到列表中。

如果抛出bad_alloc异常,则只能来自分配新节点,或来自在节点内构造元素(因为更新指针不会抛出任何内容(。

如果在分配节点对象时发生异常,那么很可能该对象尚未被移动,因为没有尝试移动构造新元素(在有节点构造它之前,您无法这样做!不能保证容器在节点内构造最终元素之前不会创建中间变量,但这将是一个质量很差的实现,因为没有理由这样做。

如果类具有非抛出移动构造函数,那么您就知道异常一定来自节点分配。否则,如果异常来自构造元素,则 rvalue 参数的状态将由相关类的异常安全保证决定。

简而言之,如果std::list实现不好,或者插入的对象在移动构造期间可能会抛出并且没有强大的异常安全保证,那么右值参数可能会处于移自状态。但除此之外,您应该能够依赖它未修改。

这取决于为什么抛出异常,但一般来说,否。移动操作后,对象处于未指定状态。请参考这个问题。

容器可能会也可能不会使用 SomeClass 的 move 构造函数,但是当您使用 std::move 时,您将对象标记为右值引用,您不应该在使用对象(或传递它(作为右值引用后使用它。

作为一种好的做法,如果您考虑重用对象,则永远不要使用 std::move。

IIRC标准规定,从中移出的对象保持未指定的状态。这意味着您可以调用成员方法,只要它们不依赖于对象的特定状态,它们就应该正常工作。需要参考