我偶然发现了一个用于stack<>::pop()的接口方法的线程安全堆栈实现:
void pop(T& value)
{
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw empty_stack();
value=std::move(data.top()); <----- why not just value = data.top()?
data.pop();
}当然,我的问题与并发无关,但是为什么将堆栈顶部的值移到变量值中呢?我的理解是,一旦它被移动,你就不能弹出它,因为它已经不在了。
这要么是我发现它的源头上的错误,要么是如果有人能向我解释的话,我会心存感激。
谢谢你,阿敏
发布于 2019-12-16 10:57:11
value=std::move(data.top());<-为什么不只是data.top= data.top()?
这在很大程度上取决于T是什么,基本上,如果移动构造函数存在,它将尝试使用移动构造函数,T(T &&mv)而不是复制构造函数T(const T &cp)。
在这两种情况下,将为data.pop();行上的原始对象调用data.pop();。
首先,使用移动构造函数可能是必需的。有些对象是可移动的,但不能复制,例如unique_ptr。
第二,在提供搬迁的情况下,往往效率更高。例如,假设T是一个std::vector,复制构造函数将分配另一个数组,然后对每个元素进行复制(这可能也很昂贵),然后它将删除原始数组。这太浪费了。
move构造函数只是通过将内部数据数组从一个向量移动到一个新的向量来保留原始元素,并将原始元素(在本例中将被删除)保持在一个未指定但有效的状态(可能是“空”)。
它可能看起来像:
template<typename T> class vector
{
public:
vector<T>(vector<T> &&mv)
: arr(mv.arr) , arr_len(mv.arr_len), arr_capacity(mv.arr_capacity)
{
mv.arr = nullptr;
mv.arr_len = 0;
mv.arr_capacity = 0;
}
...
private:
T *arr;
size_t arr_len;
size_t arr_capacity;
};由于在一般情况下,原始对象状态是“未指定的”,如果要继续使用原始对象,则必须小心。像在pop案例中那样销毁它是可以的,就像任务一样。
T tmp = std::move(some_value);
some_value.foo(); // In general, what state some_value is in is unknown, this might vary even from compiler to compiler
some_value = some_other_value; // But assignment should work
some_value.foo(); // So it is now in a known state例如,可以在不复制任何“内容”的情况下实现“交换”。
template<typename T> void swap(T &a, T &b)
{
T tmp = std::move(a);
a = std::move(b);
b = std::move(tmp);
}许多类型将被更多地指定,例如对于std::vector,它承诺是empty()。
std::vector<T> tmp = std::move(some_array);
assert(some_array.empty()); // guaranteed
some_array.push_back(x); // guaranteed to have one element你不能弹出它,因为它已经不在那里了。
因此,重要的一点是,data.top()不移除元素,因此它仍然存在。而移动并没有真正删除这个东西,只是让它处于某种未指定的状态。
并发安全堆栈接口方法
在并发这个单独的主题上,这里的内容是访问值的top,以及删除它的pop处于相同的锁下。为了安全起见,对此实例堆栈的所有访问都必须使用相同的锁实例,因此确保任何data.push也持有该锁。
https://stackoverflow.com/questions/59354884
复制相似问题