在 Java 中,Deque
该类具有用于实际返回返回元素的末端的移除方法。在 C++ 中,似乎实现相同行为的唯一方法是首先显式复制元素然后弹出它。
std::deque<int> myDeque;
myDeque.push_back(5);
int element = myDeque.back();
myDeque.pop_back();
有没有一种机制可以同时做到这两点?
您可以编写自己的包装函数模板:
// Precondition: !container.empty()
// Exception safety: If there is an exception during the construction of val,
// the container is not changed.
// If there is an exception during the return of the value,
// the value is lost.
template <typename C>
auto back_popper(C & container) -> decltype(container.back())
{
auto val(std::move(container.back()));
container.pop_back();
return val;
}
用法:
auto element = back_popper(myDeque);
首先,您无法解决促使和分离的根本问题,back()
即pop_back()
元素的复制或移动构造函数可能会引发异常,并且在发生这种情况时您可能会丢失弹出的元素。您可以通过返回非抛出对象(例如 a )来逐个减轻它unique_ptr
,这将权衡丢失元素以进行动态分配的风险,但显然这是您必须做出的个人选择不适合你。
例如:
// Guarantees that you either get the last element or that the container
// is not changed.
//
template <typename C>
auto expensive_but_lossless_popper(C & container)
-> typename std::unique_ptr<decltype(container.back())>
{
using T = decltype(container.back());
std::unique_ptr<T> p(new T(std::move(container.back())));
container.pop_back();
return p; // noexcept-guaranteed
}
编辑:正如@Simple 建议的那样,我std::move
在电话周围添加了。back()
这是合法的,因为不再需要 move-from 元素,并且许多现实世界的类都带有 noexcept 移动构造函数,因此这涵盖了大量情况,而“无损”解决方法只为少数情况提供了优势没有 noexcept 动作的“奇怪”类型。
按照设计,C++ 不提供这种开箱即用的机制来确保异常安全:
当您尝试实现它时,您首先制作元素的副本,然后弹出容器,最后您要将对象返回给调用者。但是当最后一个操作的复制构造函数抛出异常时会发生什么?该对象不再在容器中,作为调用者的您没有收到它的副本。为了防止这种情况并为容器的操作提供强大的异常保证,返回同时弹出的元素的操作不会被直接支持。
如果您不想失去异常安全性,可以使用 SFINAE 限制此方法:
//
// Precondition: !container.empty()
//
template <typename C>
auto back_popper(C & container)
-> typename std::enable_if<std::is_nothrow_move_constructible<
decltype(container.back())>::value,
decltype(container.back())>::type
{
auto val = std::move(container.back());
container.pop_back();
return val;
}
或static_assert
(自己解决)。(注意:编辑以避免任何副本,以防有移动构造函数,请遵循 Simple 的评论。)