Ошибка C2783: "_Ty && std:: forward (remove_reference<_Ty>:: type &&) throw ()": не удалось вывести аргумент шаблона для "_Ty"
У меня есть шаблонная реализация параллельной очереди, которая имеет функцию push, которая выглядит следующим образом:
template <typename T>
class concurrent_queue
{
public:
// other code...
void push(const T& item)
{
std::unique_lock<std::mutex> mlock(mutex);
queue.push_back(std::forward(item));
mlock.unlock();
notEmpty.notify_one();
}
private:
std::deque<T> queue;
std::mutex mutex;
// other stuff...
};
Позже я создаю его экземпляр и использую его так:
concurrent_queue<c2Type> m_queue; // c2 type is some struct declared previously
и затем я пытаюсь поместить элементы в очередь и получаю вышеупомянутую ошибку компилятора:
c2Type c2message;
// fill in the message struct...
m_queue.push(c2message);
Я успешно использовал очередь раньше как часть реализации пула потоков, где она хранилась std::function
объекты. Я не понимаю, почему он не может вывести тип в этом случае. Какие-нибудь мысли?
1 ответ
Категории значений, такие как "lvalue" и "rvalue", являются свойствами выражений. Выражения, которые называют переменные, всегда являются lvalue-выражениями, даже если они называют переменную, имеющую ссылку типа rvalue на some_type
,
Мы используем lvalue-reference и rvalue-reference для привязки различных категорий выражений: в соответствии с соглашением мы рассматриваем lvalue-ссылки как связанные с lvalue, а rvalue-ссылки - как связанные с rvalue.
std::forward
предназначен для восстановления категории значения того, что, как мы предполагаем, относится к ссылке. Например:
int i = 42;
int& l = i;
int&& r = 21;
l // this expression is an lvalue-expression
r // this expression is an lvalue-expression, too (!)
std::forward<int& >(l) // this function-call expression is an lvalue-expression
std::forward<int&&>(r) // this function-call expression is an rvalue-expression
std::forward
будучи "обычной функцией", не может восстановить категорию значения просто с помощью аргумента. Оба аргумента являются lvalue-выражениями. Вы должны указать, какую категорию значений вы хотите восстановить, вручную указав аргумент шаблона.
Это имеет смысл, только если у нас есть ссылка, в которой мы априори не знаем, является ли это rvalue-ссылкой или lvalue-ссылкой. Это имеет место при написании функции, которая использует идеальную пересылку с пересылкой ссылок.
Кстати, мы хотим восстановить категорию значений, чтобы позволить другой функции перейти от полученного нами аргумента. Если мы получаем аргумент rvalue, мы хотим передать значение rvalue, чтобы позволить вызываемой функции перемещаться.
Для функции, подобной той, что в OP:
void push(const T& item)
Мы знаем это item
имеет ссылку типа lvalue на const T
, Поэтому нам не нужно std::forward
:
void push(const T& item) {
// ...
queue.push_back(item); // pass the lvalue argument as an lvalue
// ...
}
Если мы добавим еще одну перегрузку:
void push(T&& item)
нам все еще не нужно std::forward
, так как тип этого параметра item
всегда rvalue-ссылка на T
(при условии, T
не является ссылочным типом):
void push(T&& item) {
// ...
queue.push_back(std::move(item)); // pass the rvalue argument as an rvalue
// ...
}
Только если у нас есть что-то вроде
template<typename U>
void push(forwarding_reference<U> item)
где forwarding_reference<U>
может быть или lvalue-ссылка или rvalue-ссылка, тогда нам нужно std::forward
:
template<typename U>
void push(forwarding_reference<U> item) // not C++, read on
{
// ...
queue.push_back(std::forward<U>(item)); // pass lvalue arguments as lvalues
// and rvalue arguments as rvalues
// ...
}
Из-за деталей реализации, мы должны написать выше как:
template<typename U>
void push(U&& item) {
// ...
queue.push_back(std::forward<U>(item)); // pass lvalue arguments as lvalues
// and rvalue arguments as rvalues
// ...
}
Обратите внимание, что выше U&& item
это не rvalue-ссылка, а ссылка для пересылки. Чтобы получить ссылку на пересылку, вам нужно иметь шаблон функции с некоторым параметром типа шаблона X
и параметр функции вида X&& x
,