Why is std::remove_reference used in std::move?

Viewed 492

I tried implementing std::move, which uses std::remove_reference, however it seems to work without it. Please give me an exemple in which my implementation fails, where std::remove_reference is necessary.

template <class type> type && move(type & source) { return (type &&) source; }
template <class type> type && move(type && source) { return (type &&) source; }

Is std::remove_reference used only to avoid overloading std::move ?

Here is a test class to help you :

class test {
public :
    test() { }
    test(const test & source) { std::cout << "copy.\n"; }
    test(test && source) { std::cout << "move.\n"; }
};

Not a duplicate of How does std::move() transfer values into RValues? because my question includes an exemple that seems to show that std::remove_reference is useless in this case + the sub-question.

2 Answers

The implementation seems to work but both function declarations overlap.

 template <class type> type && move(type && source) { return (type &&) source; }

Here the type && source is interpreted as universal reference instead of r-value reference. Therefore, it can accept any input including l-value references and for l-value reference input it will return an l-value reference output - which is a potential issue.

It is best avoided situations where multiple template function declaration can accept the same input as it can lead to variety of issues. Although, perhaps there is a C++ standard rule that forces certain template function declaration called over the other when dealing with universal references. You'd need to ask a language lawyer for that info.

You can make the move implementation with a single template function declaration using std::remove_reference as follows:

  template <class type>
  std::remove_reference_t<type> && move(type && source) 
  { 
      return (std::remove_reference_t<type>&&) source; 
  }

In general, std::remove_reference helps when dealing with universal references to figure out which type was given as input and obtaining some further information from it (although, one generally uses std::remove_cv_ref_t or equivalent).

I tried implementing std::move, which uses std::remove_reference, however it seems to work without it.

Yes, it is working because you explicitly provided the overload for lvalue reference. While std::remove_reference is only relevant if you are using forwarding references.

If you take out this line: Godbolt

template <class type> type && move(type & source) { return (type &&) source; }

And call your function as:

test t2 = move(t1); //prints copy

To make this work, you will have to use std::remove_reference. Try on Godbolt:

template <class type>
std::remove_reference_t<type> && move(type && source)
{
    return
    static_cast<std::remove_reference_t<type>&& >(source);
}

Related