Это потому, что вы ничего не перемещаете в своей функции. Они получают const T&
, но константы не могут быть перемещены. Вызов std::move
на const
не будет двигаться и абсолютно ничего не делает.
Видите, движение - это просто приведение к значению. Фактическое перемещение происходит в конструкторе перемещения и назначении перемещения. Такие конструкторы перемещения объявляются так:
unique_ptr(unique_ptr&& other);
Как видите, это неконстантная ссылка. Поскольку вы не можете вызвать конструктор перемещения, он пытается вместо этого скопировать. Это источник ошибки.
Как вы можете исправить это тогда?
Просто добавьте необходимые перегрузки и удалите лишние ходы:
template <typename T>
class AtomicQueue {
std::mutex m;
std::queue<T> q;
T value_if_empty;
public:
// copy, it's an lvalue
AtomicQueue(const T& emptyval) : value_if_empty(emptyval) {};
// move, it's an rvalue
AtomicQueue(T&& emptyval) : value_if_empty(std::move(emptyval)) {};
void push(const T& t)
{
m.lock();
q.push(t); // same here
m.unlock();
}
void push(T&& t)
{
m.lock();
q.push(std::move(t)); // same here
m.unlock();
}
T pop()
{
T a = std::move(value_if_empty);
m.lock();
if (!q.empty())
{
a = std::move(q.front());
q.pop();
}
m.unlock();
return a;
}
};
Живой пример
Также обратите внимание, что в вашем классе есть фундаментальная ошибка. Посмотрите на эту строку:
T a = std::move(value_if_empty);
Если pop
вызывается более одного раза, value_if_empty
будет перемещено из значения, и вы вернете его. Ваша функция pop
может быть вызвана только один раз перед возвратом неопределенных значений.
Вам нужно будет либо скопировать value_if_empty
, создать его по умолчанию или заменить его заводской функцией, которая будет возвращать новое значение в возьмите, если пусто.
Моим любимым решением было бы построить его по умолчанию.
Вот, тем не менее, пример с фабричной функцией:
template <typename T>
class AtomicQueue {
std::function<T()> make_empty_value;
public:
T pop()
{
T a = make_empty_value();
// ...
}
};
Затем передайте его вашему классу при его создании:
AtomicQueue<std::unique_ptr<Class1>> B([]{ return std::unique_ptr<Class1>{nullptr}; });
Если вы хотите избежать накладных расходов на std::function
, вы можете заменить переменную-член параметром шаблона типа lambda.