Reputation: 10539
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
Upvotes: 6
Views: 863
Reputation: 1432
If you want to use perfect forwarding, then I suggest you use emplace
method of queue
class. emplace
method forwards given argument to T
constructor. There will be no need to check whether T
is same as U
. It should compile as long as T
is constructable from U
. Moreover, you can use variadic template arguments if you wish.
template<typename... Args>
void push(Args&&... args)
{
container_.emplace(std::forward<Args>(args)...);
}
Therefore, you can push whatever you want, as long as T is constructable from the given arguments.
Upvotes: 0
Reputation: 20513
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
^^^^^^^^
Upvotes: 7