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

时间:2012-10-31 11:33:18

标签: 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;
}

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

3 个答案:

答案 0 :(得分: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);
}

答案 1 :(得分:3)

这是正确的(除了整个异常安全的事情)并且它将被移动(如果它支持移动)。

正如您所发现的那样,如果移动可以抛出,则无法提供强大的异常保证 - 在这种情况下您必须诉诸复制。然而,投掷动作是非常罕见的,所以我不会想太多。

答案 2 :(得分:2)

1)将被移动,因为unique_ptr已移动c-tor。

2)

来自n3337 20.7.1.2

unique_ptr(unique_ptr&& u) noexcept;
unique_ptr& operator=(unique_ptr&& u) noexcept;