Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When should I use remove_reference and add_reference?

Tags:

c++

c++11

I'm looking at [VC10's] unique_ptr and they do a couple things I don't understand:

typedef typename tr1::remove_reference<_Dx>::type _Dx_noref;

_Dx_noref& get_deleter()
    {   // return reference to deleter
    return (_Mydel);
    }

unique_ptr(pointer _Ptr,
    typename _If<tr1::is_reference<_Dx>::value, _Dx,
        const typename tr1::remove_reference<_Dx>::type&>::_Type _Dt)
    : _Mybase(_Ptr, _Dt)
    {   // construct with pointer and (maybe const) deleter&
    }

typename tr1::add_reference<_Ty>::type operator*() const
    {   // return reference to object
    return (*this->_Myptr);
    }

Wouldn't just writing _Dx& or _Ty& be the same thing?

I actually do understand why they did it here though:

unique_ptr(pointer _Ptr, typename tr1::remove_reference<_Dx>::type&& _Dt)
    : _Mybase(_Ptr, _STD move(_Dt))
    {   // construct by moving deleter
    }
like image 565
David Avatar asked Jan 14 '12 04:01

David


3 Answers

get_deleter

Any reference is removed from the return type, then a reference is added back. In conformant C++11, adding a & to an existing & (or &&) produces a &. In C++03 however, that would be forming a reference to reference type, which was illegal. Likely MSVC is using the old rules, or that code was written when it did and remains because it is harmless.

constructor

Here they remove the reference, add const, and then add the reference back, to be passing by const reference. This is because adding const directly to a reference type does nothing! (§8.3.2/1) In either C++11 or C++03, the parameter declaration would be valid but would not add a const, if the reference weren't removed and replaced.

operator*

This is essentially the same as get_deleter, but they went about it a different way, and _Ty cannot be a reference type to begin with. It looks to me like _Ty& would suffice, but it's their prerogative.

like image 105
Potatoswatter Avatar answered Nov 20 '22 07:11

Potatoswatter


Here's an example, possibly archetypal, for why we need remove_reference, in the implementation of std::move: The goal is to return an rvalue-reference type, based on the deduced type of the function argument.

Example: Foo x; move(x); Here move(x) should return a type Foo&&. But the argument of move is an expression of type Foo&. So how can the move function deduce the right type?

The first attempt is to use ordinary template argument deduction and use a cast:

template <typename T> T && move(??? x) { return static_cast<T&&>(x); }

But what should go into ???? If we say T x, then T will be deduced as Foo&; if we say T & x, then T = Foo, and if we say T && x, it won't match at all. The second version, T & x, appears to be useful.

But then the function doesn't work on rvalues to begin with (e.g. move(Foo(...)). In this case, we want T && x so that T = Foo and T&& = Foo&& as desired. We could have two overloads, but having multiple overloads is undesirable because it increases complexity needlessly. And finally, if someone were to specify the template paramete explicitly as move<Foo&>(x), the function would never work, because when T = Foo&, then T&& = Foo& as well.

So in comes remove_reference:

template <typename T>
typename std::remove_reference<T>::type && move(T && x)
{
    return static_cast<typename std::remove_reference<T>::type &&>(x);
}

First off, the new reference collapsing rules imply that T is deduces as either Foo& or Foo&& in the two cases. Then, remove_reference strips the reference and gives type Foo in any case, and adding && makes the desired Foo&& return type.

In an oversimplified summary: we need remove_reference because (Foo&)&& is Foo& and not Foo&&. If you ever write template code that needs the base type of a template paramter that could be deduced as either U& or U&&, you can use this model.

like image 29
Kerrek SB Avatar answered Nov 20 '22 08:11

Kerrek SB


template class add_reference has specialization for void, const void and const volatile void because reference of void type (void&) is not allowed. If _Ty& is used, it would generate a compilation error when _Ty = void.

like image 1
gest Avatar answered Nov 20 '22 08:11

gest