我正在编写一个带有以下函数的互斥保护堆栈,用于从可能失败的顶部弹出一个值:
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)我担心异常安全。如果移动抛出,则不会弹出堆栈,但最高值可能处于半移动状态。这是可能的,我该如何解决?
答案 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;