Std: forward within 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 class"); container_.push(std::forward<U>(value)); } }; 

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

+6
source share
2 answers

The implementation seems right to me and does the job.

However, providing different implementations for lvalues ​​and rvalues ​​might be a good idea in your case. The main reason I can think of is that the output of template type arguments does not work for braced-init lists. Consider:

 struct foo { foo(std::initializer_list<int>) { } }; // ... foo f{1, 2, 3}; // OK BlockingQueue<foo> b; 

With OP code (*)

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

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

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

Then the line that was used for the failure will work fine.

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

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

the same behavior would be observed as described above.

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

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

 typename std::remove_reference<U>::type>::value ^^^^^^^^ 
+7
source

If you want to use perfect forwarding, I suggest you use the emplace method of the queue class. emplace method passes this argument to the T constructor. There is no need to check if T the same as U It must compile as long as T can be built from U Moreover, you can use the variational arguments of the template if you want.

 template<typename... Args> void push(Args&&... args) { container_.emplace(std::forward<Args>(args)...); } 

Therefore, you can click whatever you want until T is constructed from the given arguments.

0
source

Source: https://habr.com/ru/post/957173/


All Articles