2

I would like to have member function that accepts both left and right references to some class and moves it's object. Currently I have two separate functions:

void ThrSafeQueue::push(Obj&& x) {
  lock_guard(mutex_);
  queue_.push(x);
}
void ThrSafeQueue::push(Obj& x) {
  lock_guard(mutex_);
  queue_.push(std::move(x));
}
yanpas
  • 2,155
  • 1
  • 17
  • 26

2 Answers2

4

This is what a universal reference is for:

template<typename T>
void ThrSafeQueue::push(T && x) {
  lock_guard(mutex_);
  queue_.push(std::forward<T>(x));
}

The syntax seems misleading. Here && does not actually designate an rvalue reference, but a universal reference that will bind to either an lvalue or an rvalue, depending on the template call.

std::forward will, depending on whether its template is an rvalue on an lvalue, either degenerate to std::move, or a no-op in a case of an lvalue, exactly what you want.

This example will actually attempt to forward anything, and not just an Obj, to push(). This is probably ok, but if you would like to restrict the invocation of the template to be used only with Objs, the usual tricks involving std::enable_if and std::is_same can be employed, if needed.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
1

The first option is to use template member function (forwarding references), but it can't be virtual and may accept some other types instead of Obj in some cases:

template <typename T>
void ThrSafeQueue::push(T&& x) {
  lock_guard(mutex_);
  queue_.push(std::move(x));
}

The second option is to leave only function accepting right reference and static_cast<Obj&&> all left references. Instead of static_cast you may use std::move:

ThrSafeQueue t;
t.push(Obj{});
Obj o;
t.push(std::move(o));
yanpas
  • 2,155
  • 1
  • 17
  • 26
  • Do note you can use SFINAE to constrain the template function. Also the references are called lvalue and rvalue references. – NathanOliver Feb 01 '17 at 12:31