Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std:forward inside a template class

 template<typename T>
   class BlockingQueue
   { 
       std::queue<T> container_;

       template< typename U >
       void push(U&& value)
       {
           static_assert(std::is_same<T, typename std::remove_reference<U>::type>::value,"Can't call push without the same parameter as template parameter's class");

           container_.push(std::forward<U>(value)); 
       }
};

I would like BlockingQueue::push method be able to handle both rvalue and lvalue reference of object of type T to forward it to std::queue::push correct version. Is it preferable to do like the above code, or to provide two versions of push method inside my BlockingQueue class ? One for lvalue and one for rvalue

like image 375
Guillaume Paris Avatar asked Nov 01 '13 09:11

Guillaume Paris


1 Answers

The implementation seems correct to me and does the job.

Nevertheless, providing different implementations for lvalues and rvalues might be a good idea in your case. The main reason (that I can think of) is that deduction of template type arguments doesn't work for braced-init-lists. Consider:

struct foo {
    foo(std::initializer_list<int>) {
    }
};

// ...
foo f{1, 2, 3};    // OK

BlockingQueue<foo> b;

With the OP's code (*)

b.push(f);         // OK
b.push({1, 2, 3}); // Error

If instead, the following overloads of BlockingQueue::push are provided:

void push(const T& value) {
    container_.push(value); 
}

void push(T&& value) {
    container_.push(std::move(value)); 
}

Then the line that used to fail will work fine.

The same arguments apply to aggregates. For instance, if foo was defined as

struct foo {
    int a, b, c;
};

one would observe the same behaviors described above.

My conclusion is that, if you want BlockingQueue to support more types (including aggregates or types with constructors taking std::initializer_lists), then it's better to provide two different overloads.

(*) A small correction in the OP's code: in the static_assert you need to use typename

typename std::remove_reference<U>::type>::value
^^^^^^^^
like image 66
Cassio Neri Avatar answered Sep 21 '22 23:09

Cassio Neri