std::remove_reference explained?

蓝咒 提交于 2021-02-04 09:05:53

问题


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.


回答1:


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&&)
{
    // ...
}



回答2:


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



来源:https://stackoverflow.com/questions/17000179/stdremove-reference-explained

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!