我遇到了 stack<>::pop() 的接口(interface)方法的线程安全堆栈实现:
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();
}
当然,我的问题与并发无关,但是为什么将堆栈顶部的值移动到变量值中呢?我的理解是一旦它被移动,你就不能弹出它,因为它不再存在。
要么是我找到它的来源中的错误,要么如果有人可以向我解释,我将不胜感激。
谢谢,
胺
最佳答案
value=std::move(data.top()); <----- why not just value = data.top()?
这在很大程度上取决于
T
是,基本上它会尝试使用移动构造函数,T(T &&mv)
而不是复制构造函数T(const T &cp)
如果移动构造函数存在。在这两种情况下
~T()
将为 data.pop();
上的原始对象调用线。首先,使用移动构造函数可能是必填 .有些对象是可移动的,但不可复制,例如一个
unique_ptr
.其次,在提供移动的地方,它通常更有效。例如说
T
是 std::vector
,复制构造函数将分配另一个数组,然后复制每个元素(这也可能很昂贵),然后无论如何都会删除原始数组。这是相当浪费的。移动构造函数只是通过将内部数据数组从一个 vector 移动到一个新 vector 来保留原始元素,并将原始元素(在这种情况下无论如何都将被删除)处于未指定但有效的状态(可能是“空”) .
它可能看起来像:
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
, 它 promise 是 empty()
.std::vector<T> tmp = std::move(some_array);
assert(some_array.empty()); // guaranteed
some_array.push_back(x); // guaranteed to have one element
you can't pop it as it is no longer there.
所以这里重要的一点是
data.top()
不删除元素,所以它仍然存在。并且移动实际上并没有删除这个东西,只是让它处于某种未指定的状态。Concurrency-safe stack interface method
在并发的单独主题上,这里的东西是
top
访问该值和 pop
删除它在同一个锁下。为了安全起见,对该实例堆栈的所有访问都必须使用同一个锁实例,因此请确保任何 data.push
还持有锁。
关于c++ - 并发安全栈接口(interface)方法 : correct or not?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59354884/