Error C2783: '_Ty && std :: forward (remove_reference <_Ty> :: type &&) throw ()': failed to deduce template argument for '_Ty'
I have a boilerplate implementation of a parallel queue with a push function that looks like this:
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... };
Subsequently, I create an instance and use it like this:
concurrent_queue<c2Type> m_queue; // c2 type is some struct declared previously
and then I try to push the items in the queue and I get the above compiler error:
c2Type c2message; // fill in the message struct... m_queue.push(c2message);
I've used the queue successfully before, as part of the thread pool implementation where it stored objects std::function
. I don't understand why it cannot type deduce in this case. Any thoughts?
source share
Value categories such as "lvalue" and "rvalue" are properties of expressions. Expressions that name variables are always lvalue expressions, even if they name a variable that has an rvalue reference to some_type
.
We use lvalue references and rvalue references to bind different categories of expressions: by convention, we treat lvalue references as bound to lvalues, and rvalue references bind to rvalues.
std::forward
is intended to restore the meaning category of what we are referring to. For example:
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
being a "normal function", it cannot reconstruct the category of values ββwith just an argument. Both arguments are lvalue expressions. You must specify which category of values ββyou want to restore by manually supplying a template argument.
This makes sense if we have a reference where we don't know a priori whether it is an rvalue reference or an lvalue reference. This is when you write a function that uses perfect forwarding with forward links.
By the way, we want to restore the category of values ββto allow another function to jump from the received argument. If we receive an rvalue argument, we want to pass in an rvalue to allow the called function to move.
For a function like the one in the OP:
void push(const T& item)
We know we item
have an lvalue reference to const T
. Therefore, we do not need std::forward
:
void push(const T& item) { // ... queue.push_back(item); // pass the lvalue argument as an lvalue // ... }
If we add another overload:
void push(T&& item)
we still don't need it std::forward
, since the type of this parameter is item
always an rvalue reference to T
(assuming it is T
not a reference type) :
void push(T&& item) { // ... queue.push_back(std::move(item)); // pass the rvalue argument as an rvalue // ... }
Only if we have something like
template<typename U> void push(forwarding_reference<U> item)
where forwarding_reference<U>
can be either an lvalue reference or an rvalue reference, then we need 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 // ... }
Because of the implementation details, we have to write above:
template<typename U> void push(U&& item) { // ... queue.push_back(std::forward<U>(item)); // pass lvalue arguments as lvalues // and rvalue arguments as rvalues // ... }
Note that the above is U&& item
not an rvalue reference, but a forwarding reference. To get the link to the forwarded you need to have a function template with a template type X
parameter and a form function parameter X&& x
.
source share