异常安全移动物体离开堆栈

jar*_*ond 3 c++ c++11

我正在编写一个带有以下函数的互斥保护堆栈,用于从可能失败的顶部弹出一个值:

bool try_pop(T& value)
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (ctr_.empty())
        return false;
    value = std::move(ctr_.back());
    ctr_.pop_back();
    return true;
}
Run Code Online (Sandbox Code Playgroud)

我正在使用a std::vector作为底层容器.为了在堆栈中存储不可复制的T(例如std::unique_ptr),我习惯于std::move从向量的背面取T,否则进行复制.两个问题:a)这是正确的吗?T会被移动还是被复制?b)我担心异常安全.如果移动抛出,则不会弹出堆栈,但最高值可能处于半移动状态.这是可能的,我该如何解决?

Dav*_*e S 7

a)假设它有一个移动构造函数,它将被移动.对于已定义复制构造函数但未定义移动构造函数的类型,将复制它.

b)如果你需要强大的异常保证,那么你应该使用std::move_if_noexcept它只在输入提供noexcept()移动构造函数时启用移动.这样,如果移动构造函数可以抛出,它将诉诸于复制,因此如果抛出异常,则对象在堆栈上保持不变. std::move_if_noexcept明确提供,以帮助在这种情况下提供强有力的保证.

编辑:正如Howard Hinnant指出的那样,当前的代码示例是使用移动赋值,而不是移动构造,因此std::move_if_noexcept不太可能做你想要的.要在使用赋值时解决它,您需要编写自己的包装器,它基于std::move_if_noexcept:

template <class T> typename std::conditional<
!std::is_nothrow_move_assignable<T>::value && std::is_copy_assignable<T>::value,
const T&, T&&>::type move_if_assign_noexcept(T& x) noexcept {
   return std::move(x);
}
Run Code Online (Sandbox Code Playgroud)

  • 使用移动分配,而不是移动构造.因此,对于强异常安全,`is_nothrow_move_assignable <T> :: value`必须为true,而不是`is_nothrow_move_constructible <T> :: value`.`std :: move_if_noexcept`测试构造,而不是赋值.所以jarmond真的需要构建一个测试`is_nothrow_move_assignable`的变体. (2认同)