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?

+3


source share


1 answer


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

.

+7


source







All Articles