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
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_list
s), 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
^^^^^^^^
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With