Why passing rvalue reference (X&&) is AS IF passing lvalue reference (X&)? - c++

While I was studying rvalue reference, I found a strange answer from stackoverflow.
The questioner wants to avoid code duplication between a function that receive parameter as a lvalue reference, and another one is a function that receive a rvalue reference. Both functions do the same thing.
Here is the issue:-
void foo(X& x) { /*complex thing*/ } //#A
void foo(X&& x) { /*complex SAME thing*/ } //#B
Here is the proposed solution. It is modified a bit by me:-
void foo(X& x) { /*complex thing*/ } //#A
void foo(X&& x) { foo(x); } //#B
Question
Why doesn't my version lead to stack overflow exception?
In my version, why foo#B call foo#A, but not foo#B?
More specifically, which C++ rule enforces this behavior?

According to the rule of value categories, as a named parameter, x is an lvalue. Then foo#A will be invoked, because x could be bound to lvalue-reference, but not rvalue-reference.
Note that the fact that x is declared as an rvalue-reference has nothing to do with the value category of x.
lvalue
The following expressions are lvalue expressions:
the name of a variable or a function in scope, regardless of type, such as std::cin or std::endl. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression;
You can use std::move to make it an xvalue (rvalue), then foo#B will be selected (as you expected).

Although you have an answer already saying where the C++ standard specifies this, let me also answer why it specifies this.
The idea behind rvalue reference function parameters is that the functions may assume the referenced object, or at least its contents, will no longer be used after the function returns.
Now suppose you have
void f(const X&x); // doesn't modify x
void f(X&&x); // clears x's data
and you try
void g(X&&x) {
f(x);
std::cout << "we just called f(" << x << ")" << std::endl;
}
This wouldn't be useful if it called f(X&&), because x's contents would be gone before they could get printed.
g needs to explicitly tell f that it's okay to take over the object. f cannot assume that g won't need it again later.
It might work if the compiler could somehow figure out that x is no longer needed, when g's body no longer refers to x, but that's a very complicated problem to solve and unless the rules spell out exactly when it should and shouldn't be done, compilers won't implement it the same way, making it very likely that code that works on one compiler breaks on the next.

Related

Can a lvalue-ref-qualified function be used directly in a rvalue-ref-qualified function?

I have been writing the following code to support function calls on rvalues without having to std::move explicitly on the return value.
struct X {
X& do_something() & {
// some code
return *this;
}
X&& do_something() && {
// some code
return std::move(*this);
}};
But this results in having to repeat the code inside the function. Preferably, I would do something like
struct X {
X& do_something() & {
// some code
return *this;
}
X&& do_something() && {
return std::move(do_something());
}};
Is this a valid transformation? Why or why not?
Also, I can't help but feel that there's some knowledge gap w.r.t ref-qualifiers. Is there a general way (or a set of rules) of figuring out if code like this is valid or not?
Is this a valid transformation?
Yes. Inside a member function *this is always an lvalue. Even if the function is rvalue reference qualified. It's the same as
void foo(bar& b) { /* do things */ }
void foo(bar&& b) {
// b is an lvalue inside the function
foo(b); // calls the first overload
}
So you may use an lvalue ref qualified function to share an implementation.
And using std::move on the result is also no problem. The first overload can only return an lvalue reference, because as far as it knows, it was called on an lvalue. Meanwhile the second overload has an extra bit of information, it knows it was originally invoked on an rvalue. Therefore, it does an extra cast, based on the additional information.
std::move is just a named cast that turns lvalues into rvalues. Its purpose is to signal the designated object can be treated as though it's about to expire. Since you are doing this cast inside a context where you know this to be true (the member is originally called on an object that binds to an rvalue reference), it should not pose a problem.

Pass by value/reference for a parameter that will be moved within function

If you have a function parameter that is intended to be moved into a variable within the function, would you have want to use pass by reference instead of pass by value?
For example, is there ever any benefit to using
void func(T &object2move)
{
T obj{std::move(object2move)};
}
vs.
void func(T object2move)
{
T obj{std::move(object2move)};
}
In addition to the above, is the only case where you want to use the following code when you only want func to take in an rvalue?
void func(T object2move)
{
T obj{object2move};
}
Is the answer to these questions dependent on what T is?
If your function will definitely move from the given value, then the best way to express this is by taking an rvalue-reference parameter (T&&). This way, if the user tries to pass an lvalue directly, they will get a compile error. And that forces them to invoke std::move directly on the lvalue, which visibly indicates to the reader that the value will be moved-from.
Using an lvalue-reference is always wrong. Lvalue-references don't bind to xvalues and prvalues (ie: expressions where it's OK to move from them), so you're kind of lying with such an interface.

Does a temporary object gets created by calling a function with a numeric literal?

Suppose I've got something simple like:
void f(int a){...}
int main()
{
f(3);
return 0;
}
How does the initialization happen on a slightly lower abstraction level when we call f(3) ? Does a temporary object gets created with a value of 3 or does it just initialize by copy-initialization ?
Primarily the reason for asking this was when I encountered that in this code:
void f(int a){...}
void f(int&& a){...}
int main()
{
f(3);
return 0;
}
...I get an error saying it is ambiguous. Since I'm quite sure that calling the function f(int&& a) we get a creation of temporary object which gets a lifetime extension with a reference a, I would also say that calling the f(int a) also invoke the creation of a temporary. Otherwise, shouldn't compiler choose to call f(int a) as being more effective?
Also is there a book maybe which covers this topic elegantly ?
Semantically (forgetting about inlining, optimization and stuff) the first snippet does not require a temporary.
void f(int a){...}
When you call this function as in f(3), an integer object is created using (pseudo) copy-constructor of int to be used as function argument. This becomes a local parameter to the function, and it's life-time ends when function returns.
Meanwhile, the second snippet
void f(int&& a){...}
requires calling code to create a temporary int variable (as you can't bind a reference of any kind to numeric literal). After that an rvalue reference is bound to created temporary.
And to your question of 'efficiency', compilers do not select function overloads based on efficiency. They do this based on a certain ranking of conversions, and in your case, copy and a reference binding have the same rank. You can read more about overload resolution here: https://en.cppreference.com/w/cpp/language/overload_resolution

std::thread arguments (value vs. const)

When I generate a new thread (std::thread) with a function the arguments of that function
are by value - not by reference.
So if I define that function with a reference argument (int& nArg)
my compiler (mingw 4.9.2) outputs an error (in compilian-suaeli something like
"missing copy constructor" I guess ;-)
But if I make that reference argument const (const int& nArg) it does not complain.
Can somebody explain please?
If you want to pass reference, you have to wrap it into std::reference_wrapper thanks to std::ref. Like:
#include <functional>
#include <thread>
void my_function(int&);
int main()
{
int my_var = 0;
std::thread t(&my_function, std::ref(my_var));
// ...
t.join();
}
std::thread's arguments are used once.
In effect, it stores them in a std::tuple<Ts...> tup. Then it does a f( std::get<Is>(std::move(tup))...).
Passing std::get an rvalue tuple means that it is free to take the state from a value or rvalue reference field in the tuple. Without the tuple being an rvalue, it instead gives a reference to it.
Unless you use reference_wrapper (ie, std::ref/std::cref), the values you pass to std::thread are stored as values in the std::tuple. Which means the function you call is passed an rvalue to the value in the std::tuple.
rvalues can bind to const& but not to &.
Now, the std::tuple above is an implementation detail, an imagined implementation of std::thread. The wording in the standard is more obtuse.
Why does the standard say this happens? In general, you should not bind a & parameter to a value which will be immediately discarded. The function thinks that it is modifying something that the caller can see; if the value will be immediately discarded, this is usually an error on the part of the caller.
const& parameters, on the other hand, do bind to values that will be immediately discarded, because we use them for efficiency purposes not just for reference purposes.
Or, roughly, because
const int& x = 7;
is legal
int& x = 7;
is not. The first is a const& to a logically discarded object (it isn't due to reference lifetime extension, but it is logically a temporary).

Rvalue ref and perfect forwarding

I've read few papers about && and I'm just curious if having:
void fnc_1(int&& p)
{
//...
}
void fnc(int&& r)
{
fnc_1(r);//am I suppose to/should I? call it like so:fnc_1(std::forward(r))
}
or just passing 'r' is enough?
fnc_1(r) won't compile, because r is an lvalue, just like any other variable, regardless of type. Yes, that's right, named rvalue references are lvalues, not rvalues.
fnc_1(std::forward(r)) also won't compile, because std::forward is specifically designed not to infer its template argument.
To pass an rvalue, either of the following would work:
fnc_1(std::move(r))
fnc_1(std::forward<int&&>(r))
fnc_1(std::forward<int>(r))
Using std::move is the idiomatic way to cast an lvalue to an rvalue, so I would recommend using that.
The std::forward template is usually for dependent types. Please read this question carefully to see whether it applies here. This is a difficult subject to master, so feel free to update your question with relevant details about your exact problem (using rvalue references for integers isn't terribly exciting...).
I believe your question is about the understanding of the basic properties of rvalue references. The rule of thumb to remember is:
whatever has a name is a lvalue (const or not).
whatever has no name is a rvalue.
Types with && bind to rvalues.
If you have a function...
void foo(SomeClass&& x)
{
// ... then here x has type SomeClass& !
}
then inside the body, x is a name, and therefore a l value. It really has type SomeClass&. You must use std::move to turn a SomeClass& into SomeClass&&:
void bar(SomeClass&& x)
{
// Since `x` has a name here, it is a Lvalue.
// Therefore it has type SomeClass&, what the signature doesn't indicate.
// We thus have to explicitly turn it into a rvalue:
foo(std::move(x));
}