Why is std::remove_reference used in std::move? [duplicate]
Asked Answered
A

2

6

I tried implementing std::move, which uses std::remove_reference, however it seems to work without it. Please, give me an example in which my implementation will fails whithout std::remove_reference.

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 example that seems to show that std::remove_reference is useless in this case + the sub-question.

Altruist answered 4/9, 2020 at 15:3 Comment(13)
It doesn't make a difference to your question, but you might want to use static_cast over c-style cast (as this may fall back to reinterpret_cast).Sik
@SimonKraemer It doesn't answer my question, I am asking for an exemple in which my implementation fails and there is also the other question "Is std::remove_reference used only to avoid overloading std::move ?". As I said at the bottom.Altruist
@Altruist Why should it fail? It may be just an alternative implementation. But non-conformant with the Standard.Maser
@SimonKraemer also, answering the question unstead of deleting it will solve everyone's problem even faster.Altruist
@DanielLangr I don't know why it would fail, the STL uses std::remove_reference so it should mean that it is usefull.Altruist
@Altruist Do you see any particular advantage of your solution in comparison with the standard-prescribed one? Or, is it just an academic question?Maser
@DanielLangr, it is an academic question.Altruist
@Altruist The obvious disadvantage with your solution is that you need two overloads instead of just one. Yours is also more confusing since mixing forwarding reference and relying on the fact that your other overload is "more specialized" to take predesence when called with an lvalue is not obvious.Cuckooflower
@super, I just wanted to know which was is the best and it seems that removing the reference is the best.Altruist
@Altruist Sure, I think the point of my comment is to help clarify why it's better.Cuckooflower
@DanielLangr, I'm not so sure that it doesn't conform. Under the as-if rule, I don't know how the user would be able to tell the difference unless remove_reference is deemed "addressable" for whatever reason (since you're not allowed to take its address otherwise).Faerie
@Faerie The Standard prescribes the form of std::move, including its return type and what shall it actually return: eel.is/c++draft/forward. I don't think that the implementation is allowed not to follow this prescription even if it would be indistinguishable under the as-if rule.Maser
@DanielLangr, As far as I'm aware, the as-if rule is quite broad in its power to change things like that: eel.is/c++draft/intro#abstract-1. As another example, implementations can and do add parameters to other prescribed signatures for SFINAE purposes.Faerie
L
2

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);
}

Liturgical answered 4/9, 2020 at 15:36 Comment(5)
So remove_reference is acctually not necessary, using overloads is ok, it is just an alternative way, is that what you mean ?Altruist
In your case yes it is not necessary because there is a better match that you provided yourself (the lvalue reference overload). yes, it is just an alternative, however it adds to a lot of confusion so I would avoid mixing forwarding reference function with simple reference overloads :)Liturgical
remove_reference has other use cases as well. For example: If there are two parameters with different types of references then you will need remove_reference to make it workLiturgical
I didn't intend to acctually use overloads, I wanted to know which ways is better and it seems that remove_reference is the best.Altruist
There's a lot of complexity around this subject so yes it's best to try and learn. Here's a couple of related tweets if you are interested: twitter.com/NicoJosuttis/status/1300850885845753856?s=19 there's a whole book coming out on this subject xDLiturgical
R
3

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).

Romelda answered 4/9, 2020 at 15:36 Comment(5)
Ok, I don't know i it is specific to my compiler (g++) but with the tow overloads, it calls the right one.Altruist
Why should we also remove const and volatile ?Altruist
And it's remove_cvref_t.Altruist
@Altruist when you try to obtain subtypes, e.g., std::vector<type>::value_type. If there is const/reference/volatile it interferes with the process making the type inaccessible as there is no such thing as (const std::vector<type>&)::value_type.Romelda
@Altruist remove_cvref_t is a C++20 thing... didn't get to use it. Had to write myself.Romelda
L
2

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);
}

Liturgical answered 4/9, 2020 at 15:36 Comment(5)
So remove_reference is acctually not necessary, using overloads is ok, it is just an alternative way, is that what you mean ?Altruist
In your case yes it is not necessary because there is a better match that you provided yourself (the lvalue reference overload). yes, it is just an alternative, however it adds to a lot of confusion so I would avoid mixing forwarding reference function with simple reference overloads :)Liturgical
remove_reference has other use cases as well. For example: If there are two parameters with different types of references then you will need remove_reference to make it workLiturgical
I didn't intend to acctually use overloads, I wanted to know which ways is better and it seems that remove_reference is the best.Altruist
There's a lot of complexity around this subject so yes it's best to try and learn. Here's a couple of related tweets if you are interested: twitter.com/NicoJosuttis/status/1300850885845753856?s=19 there's a whole book coming out on this subject xDLiturgical

© 2022 - 2024 — McMap. All rights reserved.