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.
Related
I know that for the following function
template <typename T>
void do_something(T&& arg);
the function parameter is a forwarding reference. But in the following case is it still a forwarding reference or an rvalue reference?
template <typename T>
class MyClass
{
void do_something(T&& arg);
};
I think it is still a forwarding reference, but I'm not sure. Furthermore, I'd like to know what can be done to enforce an rvalue reference or a forwarding reference, if the result is not what I intended.
It's an rvalue reference. Forwarding references can only appear in a deduced context. This is just a member function that accepts an rvalue reference to the class template parameter.
You can't force a forwarding reference to be an rvalue reference if you want to maintain template argument deduction for functions. If you don't mind specifying the template argument all over the place, then this will always and only ever give an rvalue reference:
template<typename T> struct identity { using type = T; };
template<typename T> void func(typename identity<T>::type&&);
In retrospect, there actually is a way to maintain deduction but force only rvalue refs to be accepted (besides the self documenting one in Simple's answer). You can provide a deleted lvalue overload:
template<typename T>
void func(T&) = delete;
template<typename T>
void func(T&& s)
{
// ...
}
The lvalue overload is more specialized when passed an lvalue. And on account of being deleted, will give a somewhat clear error message.
Furthermore I like to know, what can be done to enforce an rvalue reference
If you always want an rvalue reference in a deduced context (and not a forwarding reference), then you can use this:
template<
typename T,
typename = std::enable_if_t<!std::is_lvalue_reference<T>::value>
>
using rval_ref = T&&;
template<typename T>
void foo(rval_ref<T> s)
{
// ...
}
foo can only be called with an rvalue, and T will not be a reference (i.e. if you call foo with std::string&&, then T will be std::string).
Here is a possible implementation of std::move(). It's not fully conforming to the details of the standard, but it's very close:
template<class T>
typename std::remove_reference<T>::type&&
myMove( T&& Arg )
{
return ( ( typename std::remove_reference<T>::type&& )Arg );
}
I don't understand why it wouldn't work if we replace typename std::remove_reference<T>::type&& by the T&&, i.e.
template<class T>
typename std::remove_reference<T>::type&&
myMove( T&& Arg )
{
return ( (T&&) Arg );
}
The issue is not really about the move, but the reference collapsing that happens with T&&.
The are nice write-ups about this here and these Q&A here, and here.
I'll focus on the cast (T&&) and why that doesn't work.
Given the reference collapsing rules listed above;
T& & becomes T&
T& && becomes T&
T&& & becomes T&
T&& && becomes T&&
The problem arises when T is deduced to be an lvalue reference, then T&& becomes T& && which collapses to T&, given the cast, you merely cast it back to an lvalue reference and you have not gained the rvalue reference that enables moving to happen.
The std::remove_reference<T>::type is there to remove all the references and the && then adds back the rvalue ref and thus ensures the correct cast is made.
The typename is there to disambiguate the member type over a member variable.
I have come across a code, where std::forward is used. I have googled about it for a longtime and not able to understand its real purpose and use.
I have seen similar threads in stackoverflow, but still not clear. Can somebody explain it with a simple example?
PS: I have gone through this page, but still not able to appreciate its use. Please do not flag this question duplicate and rather try to help me out.
As the page you linked poses it:
This is a helper function to allow perfect forwarding of arguments
taken as rvalue references to deduced types, preserving any potential
move semantics involved.
When you have a named value, as in
void f1(int& namedValue){
...
}
or in
void f2(int&& namedValue){
...
}
it evaluates, no matter what, to an lvalue.
One more step. Suppose you have a template function
template <typename T>
void f(T&& namedValue){
...
}
such function can either be called with an lvalue or with an rvalue; however, no matter what, namedValue evaluates to an lvalue.
Now suppose you have two overloads of an helper function
void helper(int& i){
...
}
void helper(int&& i){
...
}
calling helper from inside f
template <typename T>
void f(T&& namedValue){
helper(namedValue);
}
will invariably call the first overload for helper, since namedValue is, well, a named value which, naturally, evaluates to an lvalue.
In order to get the second version called when appropriate (i.e. when f has been invoked with a rvalue parameter), you write
template <typename T>
void f(T&& namedValue){
helper( std::forward<T>(namedValue) );
}
All of this is expressed much concisely in the documentation by the following
The need for this function stems from the fact that all named values
(such as function parameters) always evaluate as lvalues (even those
declared as rvalue references), and this poses difficulties in
preserving potential move semantics on template functions that forward
arguments to other functions.
Each expression is in exactly one of the following two value categories: lvalue or rvalue.
Normally if you call a function like:
template<typename T>
void f(T t);
template<typename T>
void g(T t)
{
f(t);
}
The value category of the argument to g is lost between the call to g and f, because named parameters, like local variables, are always lvalues.
By using std::forward and adjusting the parameter to a "universal reference" that uses reference collapsing you can preserve the value category:
template<typename T>
void f(T&& t);
template<typename T>
void g(T&& t)
{
f(forward<T>(t));
}
That's why it's called "forward", because you are "forwarding" the value category on, rather than losing it.
So in the example if you call g with an rvalue, then f will be called with an rvalue - rather than an lvalue.
It is used to preserve the exact type of an argument in templates when passing it to another function. For example:
template<class T>
void wrapper(T&& arg)
{
foo(std::forward<T>(arg)); // Forward a single argument.
}
This works as follows:
If the function wrapper gets a std::string or const std::string&, then foo is called as if arg has type of const std::string&.
If the function wrapper gets a std::string&, then foo is called as if arg has type of std::string&.
If the function wrapper gets a std::string&&, then foo is called as if arg has type of std::string&&.
The problem that std::forward solves is that by the usual rules the type of arg within function is std::string even if we pass std::string&& to wrapper. std::forward allows to inject the actual type of T, be it T, T&, const T& or T&&, to the call site.
It's basic use is you're in function g that has been called like this:
g(T1 p1, T2 p2, /* ... */);
and you want to call function f with exactly the same types:
f(T1 p1, T2 p2, /* ... */);
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.
I got next snippet from microsoft
template <typename T> struct RemoveReference {
typedef T type;
};
template <typename T> struct RemoveReference<T&> {
typedef T type;
};
template <typename T> struct RemoveReference<T&&> {
typedef T type;
};
template <typename T> typename RemoveReference<T>::type&& Move(T&& t) {
return t;
}
...
remote_integer x = frumple(5);
remote_integer&& x1 = Move(x);
and i get an error "error C2440: 'return' : cannot convert from 'remote_integer' to 'remote_integer &&'"
something changed in compilers? With std::move all goes right.
The reason your Move doesn't work, is because t is always lvalue (even when T&& resolves to, say, int&&). Even though it might seem weird, named rvalue references are indeed lvalues.
When returning from your Move, you attempt to implicitly bind lvalue to rvalue reference, which is forbidden by standard (§8.5.3). As noted in the comments, you have to cast t explicitly to rvalue reference.
Relevant parts of standard are §5/4 and §5/5, but I'm going to quote note §5/6, which sums this nicely:
In general, the effect of this rule is that named rvalue references
are treated as lvalues and unnamed rvalue references to objects are
treated as xvalues; rvalue references to functions are treated as
lvalues whether named or not.
Correct implementation is indeed:
template <typename T>
typename std::remove_reference<T>::type&& move(T&& t)
{
return static_cast<typename std::remove_reference<T>::type&&>(t);
}
As far as I remember, this code used to be valid in earlier drafts. But since the rules have changed, you have to provide explicit cast now (same applies to std::forward).