3

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

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;
}

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

4

3 回答 3

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);
}
于 2012-10-31T11:50:46.290 回答
3

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

正如您所发现的,如果移动可以抛出,则不可能提供强有力的异常保证——在这种情况下,您必须求助于复制。不过,投掷动作是非常少见的东西,所以我不会多想。

于 2012-10-31T11:44:11.153 回答
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;
于 2012-10-31T11:46:21.490 回答