std::remove_reference explained?

C++TemplatesC++11Template Specialization

C++ Problem Overview


I saw possible implementations for std::remove_reference as below

template< class T > struct remove_reference      {typedef T type;};
template< class T > struct remove_reference<T&>  {typedef T type;};
template< class T > struct remove_reference<T&&> {typedef T type;};   

Why is it that there are specializations for lvalue and rvalue reference? Won't the general template itself be sufficient and remove the reference? I'm confused here because in the T& or T&& specialization if I try to use ::type I should still get T& or T&& respectively right?

Could you explain how, why we cast to remove_reference<t>::type&& in move? (is it because that the parameter is named so it will be treated as an lvalue inside the move function?).

Also, could you point out a way whereby I can find out and print what the type is? for e.g if its an rvalue of type int then I should be able to print out that int&& was passed? (I've been using std::is_same to check but manually.)

Thank you for your time.

C++ Solutions


Solution 1 - C++

> why is it that there are specializations for lvalue and rvalue reference?

If only the primary template existed, then doing:

remove_reference<int&>::type

Would give you:

int&

And doing:

remove_reference<int&&>::type

Would give you:

int&&

Which is not what you want. The specializations for lvalue references and rvalue references allow stripping the & and the &&, respectively, from the type argument you pass.

For instance, if you are doing:

remove_reference<int&&>

The type int&& will match the pattern specified by the T&& specialization, with T being int. Since the specialization defines the type alias type to be T (in this case, int), doing:

remove_reference<int&&>::type

Will give you int.

> could you explain how, why we cast to remove_reference<t>::type&& in move?

That's because if move() were defined as follows:

    template<typename T>
    T&& move(T&& t) { ... }
//  ^^^
//  Resolves to X& if T is X& (which is the case if the input has type X
//  and is an lvalue)

Then the return type will be X& if the argument of move() is an lvalue of type X (that's how so-called "universal references"). We want to make sure that the return type is always an rvalue reference.

The purpose of move() is to give you back an rvalue, no matter what you pass in input. Since a function call for a function whose return type is an rvalue reference is an rvalue, we really want move() to always return an rvalue reference.

That's why we do remove_reference<T>::type&&, because appending && to a non-reference type is always guaranteed to yield an rvalue reference type.

> Also could you point out a way whereby I can find out and print what the type is?

I'm not sure what you mean by "print" here. There is no portable way I know of converting the name of a type to a string (no matter how you obtain that type).

If your goal is to make sure that an rvalue was passed, on the other hand, you could use a static assertion like so:

#include <type_traits>

template<typename T>
void foo(T&&)
{
    static_assert(!std::is_reference<T>::value, "Error: lvalue was passed!");
    // ...
}

Which relies on the fact that when an lvalue of type X is being passed, T will be deduced to be X&.

You could also use an equivalent SFINAE-constraint, if you only want to produce a substitution failure:

#include <type_traits>

template<typename T, typename std::enable_if<
    !std::is_reference<T>::value>::type* = nullptr>
void foo(T&&)
{
    // ...
}

Solution 2 - C++

When you treat some type as template parameter, compiler searches for the most "specialized" specialization. If you pass a int&& to this template, compiler use remove_reference<T&&> version. General specialization don't give you what you want - if you pass int&& to general specialiation, type will be int&&

If you want to print type, use typeid(some_type).name()

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKoushik ShettyView Question on Stackoverflow
Solution 1 - C++Andy ProwlView Answer on Stackoverflow
Solution 2 - C++Evgeny EltishevView Answer on Stackoverflow