从 STL stack/queue 的 top/front 移动安全吗?

Is it safe to move from the top/front of a STL stack/queue?

这个问题对堆栈和队列都适用,但为了简单起见,我在这里仅提及堆栈。

假设我们将非常量对象压入std::stack,当我们从堆栈中弹出时,在弹出之前将堆栈顶部的对象移动到一个临时变量中是否安全?在以下内容中:

std::stack<std::string> st;
st.emplace("asdf");
auto popped = std::move(st.top());
st.pop(); 

是的,如果您使用堆栈的非常量版本,那是安全的。 (对于堆栈的 const 版本,您很可能会复制对象或遇到编译错误)

由于std::stack returns 对对象的非常量引用,您可以自由修改它。这包括从对象中移出。

回想一下,从一个对象移动会使它保持有效状态(至少,如果 class 被正确实现)。它不会摧毁它。它仍然在您的堆栈顶部。它只是不包含任何确定的值。后续 pop 将毫无问题地对其调用适当的析构函数。

请注意,不允许 std::priority_queue 搬出,因为那种容器实际上关心内容。并且 - 出于这个原因,它只是 returns 一个常量引用,不能用于从中移出内容。


响应 Iamanon 的观察,std::move 可以在 const 引用上执行。事实上,您 可以 做到这一点。通常它没有用,它通常会减少到副本,而不是移动。考虑以下示例:

#include <iostream>
#include <string>
#include <stack>

class Foo {
    public:
    Foo() {
        std::cout << "created\n";
        }
    ~Foo() {
        std::cout << "destroyed  " << value << "\n";
    }
    Foo(const Foo& other) : value(other.value) {
        std::cout << "copied\n";
    }
    Foo(Foo&& other) : value(other.value) {
        other.value++;
        std::cout << "moved\n";
    }
    Foo(const Foo&& other) : value(other.value) {
        std::cout << "const-moved\n";
    }    
    int value = 0;
};

int main()
{
std::stack<Foo> st;
st.emplace();
const std::stack<Foo>& cst = st;
auto popped = std::move(cst.top());
st.pop();
}

如果您 运行 以上,将使用 const-moved 版本。但是,当您实现 const-move 构造函数时,您会意识到您实际上无法比常规复制构造函数做得更好。那是因为 other 保持不变。例如,您不能取得 other 持有的任何东西的所有权,并在 other.

内重置它

如果删除 const-move 构造函数,编译器将使用常规复制构造函数。

如果复制构造函数被删除,只提供移动构造函数 - 那么编译器会向你抛出一个错误。