从 STL 堆栈/队列的顶部/前面移动是否安全?

Iam*_*non 3 c++ stack move

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

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

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

Cyg*_*sX1 7

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

由于std::stack返回对对象的非常量引用,因此您可以自由修改它。这包括从物体移出。

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

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


响应 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();
}
Run Code Online (Sandbox Code Playgroud)

如果你运行上面的,一个 const-moved 版本将被使用。然而,当你实现你的 const-move 构造函数时,你会意识到你真的不能比普通的复制构造函数做得更好。那是因为other保持不变。例如,您不能取得任何other保全的所有权,并在other.

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

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