从 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 构造函数,编译器将使用常规复制构造函数。
如果复制构造函数被删除,只提供移动构造函数 - 那么编译器会向你抛出一个错误。
这个问题对堆栈和队列都适用,但为了简单起见,我在这里仅提及堆栈。
假设我们将非常量对象压入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 构造函数,编译器将使用常规复制构造函数。
如果复制构造函数被删除,只提供移动构造函数 - 那么编译器会向你抛出一个错误。