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
}
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.
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.
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.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With