When should I use remove_reference and add_reference? - c++

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

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.

Related

C++ Template type deduction for temporary value

#include <iostream>
#include <vector>
using namespace std;
template <typename T>
void wrapper(T& u)
{
g(u);
}
class A {};
void g(const A& a) {}
int main()
{
const A ca;
wrapper(ca);
wrapper(A()); // Error
}
Hi I have a question on why the last statement gives a compiler error.
:18:10: error: cannot bind non-const lvalue reference of type
'A&' to an rvalue of type 'A' wrapper(A());
I thought that the template type T would be deduced as const A& as the ca is also deduced as const A&. Why the type deduction fails in this case?
I thought that the template type T would be deduced as const A& as the ca is also deduced as const A&. Why the type deduction fails in this case?
Because that's not how the deduction rules work. They strive to deduce as much of a match to the function argument type as possible. A temporary is not necessarily const, it can just bind to a const reference.
But your function template does not accept by a const reference, instead it accepts by a non-const lvalue reference. So no const is gonna spring up unless the function argument is const itself (which it isn't).
The proper solution is to use a forwarding reference (an rvalue reference to a deduced template parameter):
template <typename T>
void wrapper(T&& u)
{
g(std::forward<T>(u));
}
Now u can bind to any object. And the types deduced will tell you the value category of the function argument, allowing you to forward that category to the function call g(...), and making sure the proper overload is picked.
By the way, in case you are curious, if you add the const directly to the temporary type, your original code will build just fine.
using CA = A const;
wrapper(CA()); // Not an error anymore
Now u ends up being a A const& and binds to the temporary just fine. But that is just a curiosity that's unlikely to be useful in practice. Use forwarding references.
Why the type deduction fails in this case?
It does not fail. T is deduced to A. Therefore, the parameter u is of type A&.
The call to wrapper() is what actually fails, since an rvalue (i.e.: A() in this case) can't be bound to a non-const lvalue reference (i.e.: the parameter u).
The error message is trying to convey:
A non-const reference cannot bind to a temporary value because the temporary object's lifetime would have expired before the control reaches the function.
The variable ca is not a temporary, because it has a name that can be referred to in main.
It's lifetime does not expire till the end of main, and so can be safely referenced from within wrapper.
Template type deduction does not drop const since it would be a violation of const-correctness for ca.
For wrapper(A());, the type parameter T would be deduced as A. Since the temporary has no const-ness, so the parameter u would be deduced to a A&, but a since non-const reference cannot bind to a temporary value for the reason mentioned above, there is no wrapper function that is viable to be called using the argument A().
Now, C++ features the extension of lifetime for a temporary object if you are only reading from it. This is the reason why const T& binds to temporary objects. In your case the nameless temporary object's lifetime is extended till the end of the wrapper function.
If you explicitly set the type parameter to be const T& the following:
template <typename T>
void wrapper(const T& u)
{
g(u);
}
class A {};
void g(const A& a) {}
int main()
{
const A ca;
wrapper(ca);
wrapper(A()); // Error no more.
}
would compile just fine.
[C++11]
For wrapper(A());, the type parameter T would still be deduced as A, and the parameter u would be of type A&&, called an rvalue reference to A.
Suppose we add another overload to wrapper so that the following:
template <typename T>
void wrapper(const T& u)
{
g(u);
}
template <typename T>
void wrapper(T&& u)
{
g(u);//u is an lvalue since it has a name.
}
exists.
You can expect wrapper(A()); to compile, preferring the rvalue overload, because the rvalue reference is an lvalue in wrapper as it has a name.

std::is_pointer check with universal references

I have a function I've written to check if a given variable is of pointer type:
template<typename T>
void CheckIfPointer(T&& value)
{
static_assert(std::is_pointer<typename std::remove_reference<T>::type>::value, "T must be of pointer type");
}
I'm using a universal reference here because I do not want to restrict the value categories that could be passed in. I noticed, however, that in this example:
char const* mystr = "Hello World";
CheckIfPointer(mystr);
Type T is actually const char *& (according to clang). So is the remove_reference the appropriate solution here? Or is there a cleaner way of checking the actual type, without references getting in the way?
Note that I'm only supporting up to C++14.
Type T is actually const char *& (according to clang).
There is a special rule in template argument deduction that was introduced to permit perfect-forwarding. In the context of template argument deduction, T&& is not an rvalue reference but a forwarding reference instead.
If an lvalue is passed to a function template taking a forwarding reference, the type parameter is deduced as T& instead of T. This allows reference collapsing to take place: T& && becomes T&.
From cppreference:
If P is an rvalue reference to a cv-unqualified template parameter (so-called forwarding reference), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction (Note: this is the basis for the action of std::forward Note: in class template argument deduction, template parameter of a class template is never a forwarding reference (since C++17))
template<class T>
int f(T&&); // P is an rvalue reference to cv-unqualified T (forwarding reference)
template<class T>
int g(const T&&); // P is an rvalue reference to cv-qualified T (not special)
int main()
{
int i;
int n1 = f(i); // argument is lvalue: calls f<int&>(int&) (special case)
int n2 = f(0); // argument is not lvalue: calls f<int>(int&&)
// int n3 = g(i); // error: deduces to g<int>(const int&&), which
// cannot bind an rvalue reference to an lvalue
}
So is the remove_reference the appropriate solution here? Or is there a cleaner way of checking the actual type, without references getting in the way?
Yes, remove_reference is appropriate here. You might want to use std::remove_reference_t to avoid the explicit typename and ::type.
Also, why are you passing pointers by forwarding reference? Are you sure you don't want to pass by value or by lvalue reference?
Consider taking a const T& instead:
template<typename T>
void CheckIfPointer(const T& value)
{
static_assert(std::is_pointer<T>::value, "T must be of pointer type");
}

Why does std::forward have two overloads?

Given the following reference collapsing rules
T& & --> T&
T&& & --> T&
T& && --> T&
T&& && --> T&&
The third and fourth rule imply that T(ref qualifer) && is the identity transformation, i.e. T& stays at T& and T&& stays at T&&. Why do we have two overloads for std::forward? Couldn't the following definition serve all purposes?
template <typename T, typename = std::enable_if_t<!std::is_const<T>::value>>
T&& forward(const typename std::remove_reference<T>::type& val) {
return static_cast<T&&>(const_cast<T&&>(val));
}
Here the only purpose the const std::remove_reference<T>& serves is to not make copies. And the enable_if helps ensure that the function is only called on non const values. I'm not entirely sure whether the const_cast is needed since it's not the reference itself that's const.
Since forward is always called with explicit template parameters there are two cases we need to consider:
forward<Type&>(val) Here the type of T in forward will be T& and therefore the return type will be the identity transformation to T&
forward<Type&&>(val) Here the type of T in forward will be T&& and therefore the return type will be the identity transformation to T&&
So then why do we need two overloads as described in http://en.cppreference.com/w/cpp/utility/forward?
Note: I am not sure if std::forward is ever used with const types, but I disabled forward in that case, because I have never seen it used like that. Also move semantics don't really make sense in that case either.
A good place to start would be Howard Hinnant's answer and paper on std::forward().
Your implementation handles all the normal use-cases correctly (T& --> T&, T const& --> T const&, and T&& --> T&&). What it fails to handle are common and easy-to-make errors, errors which would be very difficult to debug in your implementation but fail to compile with std::forward().
Given these definitions:
struct Object { };
template <typename T, typename = std::enable_if_t<!std::is_const<T>::value>>
T&& my_forward(const typename std::remove_reference<T>::type& val) {
return static_cast<T&&>(const_cast<T&&>(val));
}
template <class T>
void foo(T&& ) { }
I can pass non-const references to const objects, both of the lvalue variety:
const Object o{};
foo(my_forward<Object&>(o)); // ok?? calls foo<Object&>
foo(std::forward<Object&>(o)); // error
and the rvalue variety:
const Object o{};
foo(my_forward<Object>(o)); // ok?? calls foo<Object>
foo(std::forward<Object>(o)); // error
I can pass lvalue references to rvalues:
foo(my_forward<Object&>(Object{})); // ok?? calls foo<Object&>
foo(std::forward<Object&>(Object{})); // error
The first two cases lead to potentially modifying objects that were intended to be const (which could be UB if they were constructed const), the last case is passing a dangling lvalue reference.

Template references type deducion

I have a template function:
template<typename T>
void doSomething(T& value) {
// doSomething here
}
All is ok, but passing r-value references:
doSomething(getTempVal());
Producing no matching function for call error, because particular instantiated function template expects an l-value for 1st argument. Is there any workarounds to allow template function taking both lvalue and rvalue references without adding new template?
Yes, just use && instead of &.
It may seem odd, because you might think that this would disallow calling it with lvalues, but actually, if T is itself an lvalue reference type, then T && is just T: it doesn't become an rvalue reference type.
In other words,
template <typename T>
void f(T &&);
int main() {
int k = 1;
f(k); // okay: calls f<int&>
f(2); // okay: calls f<int>
}
Note that T can be deduced as a reference type, and the function body will need to be made to handle that.

deducing references to const from rvalue arguments

Okay, this may seem like a silly question, but here it goes:
template <typename T>
void foo(T& x)
{
}
int main()
{
foo(42);
// error in passing argument 1 of 'void foo(T&) [with T = int]'
}
What is preventing C++ to instantiate the foo function template with T = const int instead?
The problem is that template type deduction has to work out an exact match, and in that particular case, because of the reference in the signature, an exact match requires an lvalue. The value 42, is not an lvalue, but rather an rvalue, and resolving T with const int would not yield a perfect match. Since template type deduction is limited to exact matches, that deduction is not allowed.
If instead of using a literal you use a non mutable lvalue, then the compiler will deduce the type appropriatedly, as const int will become a perfect match for the argument:
const int k = 10;
foo( k ); // foo<const int>( const int & ) is a perfect match
Now there is a special rule that enables calling a function that takes a const reference (nonmutable lvalue) with an rvalue, that implies creation of a temporary lvalue which is later bound to the reference, but for that rule to kick in the function has to have that signature before hand, which is why explicitly stating that the type of the template is const int works: foo<const int>(42).
Them's the rules ;-). If you leave the compiler to deduce the type from the argument, it picks the simplest thing it can.
It doesn't seem unreasonable to me. Your template is saying it expects a non-const reference, so it doesn't compile with an rvalue.
You could either tell it what you mean at the call site: foo<int const>(42); or change your template to make it clear it doesn't need a mutable reference: template <typename T> void foo(T const & x) { }.
In C++11 you have more options for expressing what your template will and will not accept.
Be it template or normal functions, rvalue cannot be passed by reference. (so const T& works but not T&).
What is preventing C++ to instantiate the foo function template with T = const int instead?
Suppose, C++ allows and makes T = const int instead.
Now after sometime you change foo as,
template<typename T>
void foo (T& x)
{
x = 0;
}
Now compiler has to generate error. For end user the experience will be strange, as for a valid statement like x = 0; it started giving error. That could be the reason that why compiler prevents at the first stage itself!