C++ 异常安全将对象移出堆栈

C++ 异常安全将对象移出堆栈,c++,c++11,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; } boo

我正在编写一个受互斥保护的堆栈,其中包含以下函数,用于在可能出现故障时从顶部弹出一个值:

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;
}
bool try\u pop(T&value)
{
std::锁\保护锁(互斥锁);
如果(ctr_uz.empty())
返回false;
value=std::move(ctr_u.back());
ctr_u.pop_back();
返回true;
}
我使用
std::vector
作为底层容器。为了在堆栈中存储不可复制的T(例如,
std::unique_ptr
),我使用
std::move
将T从向量的背面取下,否则将进行复制。两个问题:a)这是正确的吗?将不移动或复制该文件?b) 我担心异常安全。如果move抛出,那么堆栈将不会弹出,但top值可能处于半移动状态。这可能吗?我该如何解决?

这是正确的(除了整个异常安全问题),它将被移动(如果它支持移动)

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

1)将被移动,因为
unique\u ptr
有move c-tor

(二)

来自n3337 20.7.1.2

unique_ptr(unique_ptr&& u) noexcept;
unique_ptr& operator=(unique_ptr&& u) noexcept;
a) 它将被移动,假设它有一个移动构造函数。对于已定义复制构造函数但未定义移动构造函数的类型,将复制它

b) 如果您需要强异常保证,那么应该使用
std::move_If_noexcept
,它仅在输入提供
noexcept()
move构造函数时启用移动。这样,如果move构造函数可以抛出,它将求助于创建一个副本,因此如果抛出异常,对象在堆栈上保持不变
std::move_if_noexcept
被明确提供,以帮助在这种情况下提供强有力的保证

编辑:正如Howard Hinnant所指出的,当前的代码示例使用的是移动赋值,而不是移动构造,因此
std::move_if_noexcept
可能不会执行您想要的操作。要在使用赋值时解决这个问题,您需要编写自己的包装器,它基于
std::move\u if\u 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);
}
模板类型名称std::conditional<
!std::is_nothrow\u move\u assignable::value&&std::is_copy\u assignable::value,
常量T&,T&&>:如果赋值,则键入move___noexcept(T&x)noexcept{
返回std::move(x);
}

是否有理由围绕
std::vector
而不是围绕
std::stack
构建堆栈?我认为一个移动抛出并使正在移动的对象处于半状态不是移动的有效实现。@Joachim。因为std::stack不会使用移动语义?@JoachimPileborg。正如CashCow所说,std::stack只有一个复制推送成员。@jarmond:std::stack的C++11的完整实现也应该有一个移动推送。请参见移动指定,而不是使用移动构造。因此,对于强异常安全性,
is_nothrow\u move\u assignable::value
必须为true,而不是
is_nothrow\u move\u constructible::value
<代码>标准::如果没有异常,则移动\u测试构造,而不是分配。所以jarmond真的需要构建一个变体来测试
是否是可分配的