Why does std::move take a forward reference? - c++

The implementation of std::move basically looks like this:
template<typename T>
typename std::remove_reference<T>::type&&
move(T&& t)
{
return static_cast<typename std::remove_reference<T>::type&&>(t);
}
Note that the parameter of std::move is a universal reference (also known as a forwarding reference, but we're not forwarding here). That is, you can std::move both lvalues and rvalues:
std::string a, b, c;
// ...
foo(std::move(a)); // fine, a is an lvalue
foo(std::move(b + c)); // nonsense, b + c is already an rvalue
But since the whole point of std::move is to cast to an rvalue, why are we even allowed to std::move rvalues? Wouldn't it make more sense if std::move would only accept lvalues?
template<typename T>
T&&
move(T& t)
{
return static_cast<T&&>(t);
}
Then the nonsensical expression std::move(b + c) would cause a compile-time error.
The above implementation of std::move would also be much easier to understand for beginners, because the code does exactly what it appears to do: It takes an lvalue and returns an rvalue. You don't have to understand universal references, reference collapsing and meta functions.
So why was std::move designed to take both lvalues and rvalues?

Here is some example simplified to the extreme:
#include <iostream>
#include <vector>
template<typename T>
T&& my_move(T& t)
{
return static_cast<T&&>(t);
}
int main()
{
std::vector<bool> v{true};
std::move(v[0]); // std::move on rvalue, OK
my_move(v[0]); // my_move on rvalue, OOPS
}
Cases like the one above may appear in generic code, for example when using containers which have specializations that return proxy objects (rvalues), and you may not know whether the client will be using the specialization or not, so you want unconditional support for move semantics.

It doesn't hurt.
You're simply establishing a guarantee that code will treat the result as an rvalue. You certainly could write std::move in such way that it errors out when dealing with something that's already an rvalue, but what is the benefit?
In generic code, where you don't necessarily know what type(s) you're going to be working with, what gains in expressiveness would you extract out of a bunch of "if type is rvalue do nothing else std::move" plastered everywhere when you can simply say "I promise we can think of this as an rvalue".
You said it yourself, it is nothing more than a cast. Should *_cast operations also fail if the argument already matches the intended type?

Related

std::forward acting differently

I'm currently learning perfect forwarding in c++ and I came across something that confused me. Pretty sure it's something stupid. When I used std::forward on a lvalue, it used the rvalue function. This is a pretty bad explanation so I'm just gonna show the code.
#include <iostream>
void check(int&& other) {
std::cout << "Rvalue" << std::endl;
}
void check(int& other) {
std::cout << "Lvalue" << std::endl;
}
void call(int& other) {
check(std::forward<int>(other));
}
int main()
{
int i = 4;
call(i);
}
This outputs "Rvalue". Please help me understand why.
"Perfect forwarding" is used in templates, i.e.:
template<typename T>
void func(T && arg)
{
func2(std::forward<T>(arg));
}
Outside of template context the end result drastically changes. All that std::forward does is return static_cast<T &&>, so your call function becomes nothing more than:
void call(int& other) {
check(static_cast<int &&>(other));
}
Hence you get an rvalue. The reason this works differently in templates is because a && template parameter is a forwarding reference (a fancy term for either an lvalue or an rvalue-deduced reference, depending on what gets dropped in that parameter), and because of reference collapsing rules. Briefly, when used in a template context, the end result is:
T gets deduced as either an lvalue or an rvalue reference, depending on what the parameter is.
The result of the static_cast<T &&> is an lvalue reference, if T is an lvalue reference, or an rvalue reference if T is an rvalue reference, due to reference collapsing rules.
The end result is that the same kind of a reference gets forwarded. But this only works in template context, since it requires both forwarding reference semantics and reference collapsing rules to work just right.
std::forward() isn't exactly magic. Which is one of the reasons one has to give it the appropriate type with the appropriate reference-category (rvalue-reference, lvalue-reference, no reference).
Normally, you get the type from the template-argument. If it's an auto&&-argument (C++14 lambda, C++20 abbreviated template or use of concept), one uses decltype() to get it from the function-argument.
Manually specifying it works, but goes against the spirit of using the function. std::move() or the argument itself is much easier to use in that case, depending on the template-argument you specify.
And due to how reference-collapsing and std::forward() are defined, Type and Type&& both result in an rvalue-reference, while Type& results in an lvalue-reference.

Usage of std::forward vs std::move

I always read that std::forward is only for use with template parameters. However, I was asking myself why. See the following example:
void ImageView::setImage(const Image& image){
_image = image;
}
void ImageView::setImage(Image&& image){
_image = std::move(image);
}
Those are two functions which basically do the same; one takes an l-value reference, the other an r-value reference. Now, I thought since std::forward is supposed to return an l-value reference if the argument is an l-value reference and an r-value reference if the argument is one, this code could be simplified to something like this:
void ImageView::setImage(Image&& image){
_image = std::forward(image);
}
Which is kind of similar to the example cplusplus.com mentions for std::forward (just without any template parameters). I'd just like to know, if this is correct or not, and if not why.
I was also asking myself what exactly would be the difference to
void ImageView::setImage(Image& image){
_image = std::forward(image);
}
You cannot use std::forward without explicitly specifying its template argument. It is intentionally used in a non-deduced context.
To understand this, you need to really understand how forwarding references (T&& for a deduced T) work internally, and not wave them away as "it's magic." So let's look at that.
template <class T>
void foo(T &&t)
{
bar(std::forward<T>(t));
}
Let's say we call foo like this:
foo(42);
42 is an rvalue of type int.
T is deduced to int.
The call to bar therefore uses int as the template argument for std::forward.
The return type of std::forward<U> is U && (in this case, that's int &&) so t is forwarded as an rvalue.
Now, let's call foo like this:
int i = 42;
foo(i);
i is an lvalue of type int.
Because of the special rule for perfect forwarding, when an lvalue of type V is used to deduce T in a parameter of type T &&, V & is used for deduction. Therefore, in our case, T is deduced to be int &.
Therefore, we specify int & as the template argument to std::forward. Its return type will therefore be "int & &&", which collapses to int &. That's an lvalue, so i is forwarded as an lvalue.
Summary
Why this works with templates is when you do std::forward<T>, T is sometimes a reference (when the original is an lvalue) and sometimes not (when the original is an rvalue). std::forward will therefore cast to an lvalue or rvalue reference as appropriate.
You cannot make this work in the non-template version precisely because you'll have only one type available. Not to mention the fact that setImage(Image&& image) would not accept lvalues at all—an lvalue cannot bind to rvalue references.
I recommend reading "Effective Modern C ++" by Scott Meyers, specifically:
Item 23: Understand std::move and std::forward.
Item 24: Distinguish universal references for rvalue references.
From a purely technical perspective, the answer is yes: std::forward
can do it all. std::move isn’t necessary. Of course, neither function
is really necessary, because we could write casts everywhere, but I
hope we agree that that would be, well, yucky. std::move’s attractions
are convenience, reduced likelihood of error, and greater clarity.
rvalue-reference
This function accepts rvalues and cannot accept lvalues.
void ImageView::setImage(Image&& image){
_image = std::forward(image); // error
_image = std::move(image); // conventional
_image = std::forward<Image>(image); // unconventional
}
Note first that std::move requires only a function argument, while std::forward requires both a function argument and a template type argument.
Universal references (forwarding references)
This function accepts all and does perfect forwarding.
template <typename T> void ImageView::setImage(T&& image){
_image = std::forward<T>(image);
}
You have to specify the template type in std::forward.
In this context Image&& image is always an r-value reference and std::forward<Image> will always move so you might as well use std::move.
Your function accepting an r-value reference cannot accept l-values so it is not equivalent to the first two functions.

C++ Perfect Forwarding

1)
template<typename T, typename Arg>
shared_ptr<T> factory(Arg arg)
{
return shared_ptr<T>(new T(arg));
}
2)
template<typename T, typename Arg>
shared_ptr<T> factory(Arg& arg)
{
return shared_ptr<T>(new T(arg));
}
3)
template<typename T, typename Arg>
shared_ptr<T> factory(Arg const & arg)
{
return shared_ptr<T>(new T(arg));
}
*)Why is number 3 is preferred than number 1 and number 2?
*)if factory(41) is called, why is called on rvalue?
*)# define BOOST_ASIO_MOVE_ARG(type) type&&. what's && in this case?
Actually, approach #3 is not better than 1 and 2. It all depends on T's constructor(s).
A somewhat standard way to do this is using rvalue references, which are the && you mention.
So, a better factory would be something like this: (which is what perfect forwarding actually is)
template<typename T, typename Arg>
std::shared_ptr<T> factory(Arg && arg)
{
return std::shared_ptr<T>(new T (std::forward<Arg>(arg)));
}
Due to reference collapsing rules of C++, this function can take (and forward) both rvalue refences and lvalue references, i.e., if you pass it an rvalue, it will forward that rvalue faithfully to T's constructor, and if you give it an lvalue, the two kinds of reference (Something & && arg) will collapse and the lvalue reference will be forwarded to the underlying constructor.
I think I covered your questions here, but to answer explicitly,
#3 is not the preferred method here. Even before C++11, you probably wanted both const& and & overloads of the factory function. The const& version is nicer and it will actually accept temporary values, but if your underlying T type has a constructor that accepts a non-const ref, then you would get a compile error because a const& is not implicitly casted to a &.
Because you can't take the address of the literal 41 (this is not 100% technically correct, but I think it's OK to think of lvalues and rvalues this way.)
It signifies an rvalue reference. You need to read about that; an explanation will not fit here, and there are already several great ones just a Google search away!
UPDATE: As mentioned tangentially in the comments, using make_shared can be better than constructing a shared_ptr with a just-newed pointer. make_shared might achieve better efficiency by allocating the control block (which includes the reference count) along with the object itself, which would provide better cache locality when accessing the reference counter and the object, and also may save one memory allocation. Even if the implementation has none of the optimizations, it won't be any worse than the above version. So use std::make_shared whenever possible! And this would be how you do it here:
template<typename T, typename Arg>
std::shared_ptr<T> factory (Arg && arg)
{
return std::make_shared<T>(std::forward<Arg>(arg));
}
*)Why is number 3 is preferred than number 1 and number 2?
(1) won't work if Arg is non-copyable.
(2) won't allow you to pass an rvalue, as in factory<int>(42);
Note that none of the three examples involve perfect forwarding. I'm not sure what the subject of your question refers to.
*)if factory(41) is called, why is called on rvalue?
I'm not sure I understand the question. 41 is an rvalue by definition.
*)# define BOOST_ASIO_MOVE_ARG(type) type&&. what's && in this case?
type&& is an rvalue reference to type.

A function template using std::forward and rvalue

Given the following function template from "The C++ Programming language 4th edition":
template <typename TT, typename A>
unique_ptr<TT> make_unique(int i, A && a)
{
return unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};
}
I find it difficult to understand what that actually does,
a is definitely an rvalue and therefore the make_unique function
seem to allocate its content on the heap and holding that address in a unique_ptr so we won't have to worry about deleting it. but, what does the standard library forward function does? (I guess it has something to do with a being rvalue) I tried reading at C++ documentation but I don't seem to understand that properly.
would love to get a good explanation from a more experienced C++ programmer.
thanks!
Hmmm... I'm pretty sure this isn't given as a work-around implementation of the future std::make_unique, but anyway, what the function does is pretty easy to understand, though it requires you to have prior knowledge of new C++11 features.
template <typename TT, typename A>
unique_ptr<TT> make_unique(int i, A && a)
{
return unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};
}
First of all make_unique is a function template, I really hope you already know that, as the following would require that you have at least the most basic knowledge on what templates does and how templates work.
Now to the non-trivial parts. A && a there is a function parameter. Specifically, a is the function parameter whose type is A&& which is an r-value reference. With its type being a template type parameter, we can deduce its type from whatever the caller passes as an argument to a. Whenever we have r-value reference and argument type deduction, special deduction rules and reference collapsing kicks-in and we have a so-called "universal reference" which is particularly useful for perfect forwarding functions.
Whenever we have a universal reference (a in our case), we will almost always want to preserve its original "l-valueness" or "r-valueness" whenever we want to use them. To have this kind of behavior, we should almost always use std::forward (std::forward<A>(a)). By using std::forward, a variable originally passed as an l-value remains an l-value and a variable originally passed as an r-value remains an r-value.
After that, things are just simple
return unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};
Notice the use of the braces. Instead of using parentheses, it is using C++11's uniform initialization syntax of calling constructors. With new TT{ i, std::forward<A>(a) }, you are dynamically allocating an object of type TT with the given parameters inside the braces. With unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};, you are creating a unique_ptr<TT> whose parameter is the one returned by the dynamic allocation. The unique_ptr<TT> object now then returned from the function.
Due to template argument deduction and reference collapsing rules you cannot know if a is a rvalue reference or a lvalue reference. std::forward passes the argument to the TT contrustor exactly as it was passed to make_unique. Scott Meyers calls A&& a universal reference, because it can be a lvalue ref or an rvalue ref, depended on what is passed to make_unique.
If you pass an rvalue Foo to make_unique, std::forward passes an rvalue reference.
If you pass an lvalue Foo to make_unique, std::forward passes an lvalue reference.
make_unique(1, Foo()); // make_unique(int, A&&) -> rvalue ref
Foo f;
make_unique(1, f); // make_unique(int, A&&&) -> make_unique(int, A&) -> lvalue ref
make_unique(1, std::move(f)); // make_unique(int, A&&&&) -> make_unique(int, A&&) -> rvalue ref

Can I typically/always use std::forward instead of std::move?

I've been watching Scott Meyers' talk on Universal References from the C++ and Beyond 2012 conference, and everything makes sense so far. However, an audience member asks a question at around 50 minutes in that I was also wondering about. Meyers says that he does not care about the answer because it is non-idiomatic and would silly his mind, but I'm still interested.
The code presented is as follows:
// Typical function bodies with overloading:
void doWork(const Widget& param) // copy
{
// ops and exprs using param
}
void doWork(Widget&& param) // move
{
// ops and exprs using std::move(param)
}
// Typical function implementations with universal reference:
template <typename T>
void doWork(T&& param) // forward => copy and move
{
// ops and exprs using std::forward<T>(param)
}
The point being that when we take an rvalue reference, we know we have an rvalue, so we should std::move it to preserve the fact that it's an rvalue. When we take a universal reference (T&&, where T is a deduced type), we want std::forward to preserve the fact that it may have been an lvalue or an rvalue.
So the question is: since std::forward preserves whether the value passed into the function was either an lvalue or an rvalue, and std::move simply casts its argument to an rvalue, could we just use std::forward everywhere? Would std::forward behave like std::move in all cases where we would use std::move, or are there some important differences in behaviour that are missed out by Meyers' generalisation?
I'm not suggesting that anybody should do it because, as Meyers correctly says, it's completely non-idiomatic, but is the following also a valid use of std::move:
void doWork(Widget&& param) // move
{
// ops and exprs using std::forward<Widget>(param)
}
The two are very different and complementary tools.
std::move deduces the argument and unconditionally creates an rvalue expression. This makes sense to apply to an actual object or variable.
std::forward takes a mandatory template argument (you must specify this!) and magically creates an lvalue or an rvalue expression depending on what the type was (by virtue of adding && and the collapsing rules). This only makes sense to apply to a deduced, templated function argument.
Maybe the following examples illustrate this a bit better:
#include <utility>
#include <memory>
#include <vector>
#include "foo.hpp"
std::vector<std::unique_ptr<Foo>> v;
template <typename T, typename ...Args>
std::unique_ptr<T> make_unique(Args &&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); // #1
}
int main()
{
{
std::unique_ptr<Foo> p(new Foo('a', true, Bar(1,2,3)));
v.push_back(std::move(p)); // #2
}
{
v.push_back(make_unique<Foo>('b', false, Bar(5,6,7))); // #3
}
{
Bar b(4,5,6);
char c = 'x';
v.push_back(make_unique<Foo>(c, b.ready(), b)); // #4
}
}
In situation #2, we have an existing, concrete object p, and we want to move from it, unconditionally. Only std::move makes sense. There's nothing to "forward" here. We have a named variable and we want to move from it.
On the other hand, situation #1 accepts a list of any sort of arguments, and each argument needs to be forwarded as the same value category as it was in the original call. For example, in #3 the arguments are temporary expressions, and thus they will be forwarded as rvalues. But we could also have mixed in named objects in the constructor call, as in situation #4, and then we need forwarding as lvalues.
Yes, if param is a Widget&&, then the following three expressions are equivalent (assuming that Widget is not a reference type):
std::move(param)
std::forward<Widget>(param)
static_cast<Widget&&>(param)
In general (when Widget may be a reference), std::move(param) is equivalent to both of the following expressions:
std::forward<std::remove_reference<Widget>::type>(param)
static_cast<std::remove_reference<Widget>::type&&>(param)
Note how much nicer std::move is for moving stuff. The point of std::forward is that it mixes well with template type deduction rules:
template<typename T>
void foo(T&& t) {
std::forward<T>(t);
std::move(t);
}
int main() {
int a{};
int const b{};
//Deduced T Signature Result of `forward<T>` Result of `move`
foo(a); //int& foo(int&) lvalue int xvalue int
foo(b); //int const& foo(int const&) lvalue int const xvalue int const
foo(int{});//int foo(int&&) xvalue int xvalue int
}