Mixing Rvalue and LValue reference - c++

I'm trying to better understand LValue, RValue, and how std::move works.
I have the following code
#include <string>
class A
{
public:
A() = default;
A(std::string&& aString): myString(std::move(aString)) {}
std::string myString;
};
class B
{
public:
void InitMembers(std::string& aString) { myA = A(std::move(aString));}
private:
A myA;
};
int main()
{
B b;
std::string hello;
b.InitMembers(hello);
}
my questions are:
In void InitMembers(string& aString) { myA = A(std::move(aString));} I understand that I have to use std::move to aString in order to cast aString from an LValue reference to a RValue reference. But I have some doubts regarding the meaning of aString in the InitMember scope. aString is provided as an LValue reference, but in the method scope it's considered as an LValue and that's why I have to use the std::move? Std::move should rely on reference deduction (right?), how does it deduce the type in this case? It will deduce a type "string" o "string&" since aString is provided as a LValue Reference in the method's arguments?
Why do I have to use std::move also in the constructor initializer of A? Shouldn't be enough the fact that aString is an RValue reference, which triggers the move constructor?
Isn't the following implementation good as the one above?
#include <string>
class A
{
public:
A() = default;
A(std::string& aString): myString(std::move(aString)) {}
std::string myString;
};
class B
{
public:
void InitMembers(std::string& aString) { myA = A(aString);}
private:
A myA;
};
int main()
{
B b;
std::string hello;
b.InitMembers(hello);
}
Thanks :)

Concerning
A(std::string&& aString): myString(std::move(aString)) {}
std::string&& denotes an rvalue reference to a std::string. Rvalue references only bind to rvalues (both prvalues and xvalues), so the two possible call sites will be like this:
// assuming this is defined somewhere
std::string f(void); // returns by value, i.e. `f()` is an rvalue
// call site 1
A a1{f()}; // `f()` is an rvalue (precisely a prvalue)
// assuming this
std::string s{"ciao"}; // s is an lvalue
// call site 2
A a2{std::move(s)}; // `std::move(s)` is an rvalue (precisely an xvalue)
// i.e. with `std::move` we turn s into an rvalue argument,
// so it can bind to the rvalue reference parameter
// don't expect s to be the one it was before constructing a2
In either case, what does the constructor do with aString?
Well, aString is an lvalue, because "it has a name" (the actual definition is a bit more complicated, but this is the easiest one to get started with, and it isn't all that wrong after all), so if you use it verbatim, the compiler won't assume it is bound to a temporary and it won't let myString steal it resources.
But you know that aString is bound to a temporary, because you've declared it as std::string&&, so you pass it as std::move(aString) to tell the compiler "treat this is a temporary".
Yes, technically also the compiler knows that aString is bound to a temporary, but it can't std::move it automatically. Why? Because you might want to use it more than once:
A(std::string&& aString) : myString(aString/* can't move this */) {
std::cout << aString << std::endl; // if I want to use it here
}
// yes, this is a very silly example, sorry
As regards
void InitMembers(std::string& aString) { myA = A(std::move(aString));}
aString denotes an lvalue reference to non-const std::string, so you can pass to .InitMembers only non-const lvalues.
Then inside the function you're std::moveing it to tell A's constructor "look, this is a temporary". But that also means that at the call site (b.InitMembers(hello);) you're leaving the input (hello) in a moved-from state, just like the s in the first example above. That's ok, because the caller knows that InitMembers takes its parameter by non-const lvalue reference, so it is aware that the argument they pass can be changed by the call. Just like in the previous example it's the user who's writing std::move around s, so they're supposed to know what they do.
For more details about how std::move works (and std::forward as well), I want to point you to this answer of mine.

Related

Why is Copy constructor called with rvalue

In the below code, even though Account(20, "Dave") at Line2 is rvalue, why is the copy constructor called (Line1), instead of compiler throwing error? In case of normal functions receiving rvalue, if we use lvalue reference as input parameter, compiler throws error.
#include <iostream>
#include <string>
#include <vector>
class Account
{
private:
int num;
std::string name;
public:
Account(int lnum, std::string lname) : num {lnum}, name {lname}
{
std::cout << "\n3arg constr";
}
Account(const Account &a) : Account{a.num, a.name} //Line1
{
std::cout << "\nCopy Constr";
}
};
int main()
{
std::vector<Account> myVec {};
myVec.push_back(Account(20, "Dave")); //Line2
std::cout << std::endl;
}
Yes, rvalues are moved, lvalues are copied. But when there's no according move operation, rvalues are copied as well.
The compiler will synthesize a move constructor only for such class that doesn't define any of its own copy-control members (copy-constructor, copy-assignment, or destructor), and if all the non-static members can be moved.
If a class doesn't have a move operation, the corresponding copy operation will be used, through normal function matching (T&& can convert to const T&).
Unlike copy operations, a move operation won't be implicitly defined as deleted function. A copy is simply used instead.
As for the push_back(). Vector provides guarantees that is something goes wrong in process of the push_back(), the vector, to which we push, will be left unchanged, in the absence of noexcept move constructor for a class, a copy is used instead,
What you have in Account(const Account &a) is an lvalue reference to const. Const lvalue references can bind to rvalues. Quoting the reference (emphasis mine)
Rvalue references can be used to extend the lifetimes of temporary
objects (note, lvalue references to const can extend the lifetimes of
temporary objects too, but they are not modifiable through them):
struct Foo{};
void bar(Foo && f) {}
void baz(Foo const&) {}
void qux(Foo&) {}
int main() {
bar(Foo{}); //rvalue reference binds to rvalue
baz(Foo{}); //const rvalue reference binds to rvalue
//Error: cannot bind non-const lvalue reference of type 'Foo&' to an rvalue of type 'Foo'
// qux(Foo{});
}

Why is the copy constructor called instead of the move constructor when returning?

Let’s say I have the class MyClass with a correct move constructor and whose copy constructor is deleted. Now I am returning this class like this:
MyClass func()
{
return MyClass();
}
In this case the move constructor gets called when returning the class object and everything works as expected.
Now let’s say MyClass has an implementation of the << operator:
MyClass& operator<<(MyClass& target, const int& source)
{
target.add(source);
return target;
}
When I change the code above:
MyClass func()
{
return MyClass() << 5;
}
I get the compiler error, that the copy constructor cannot be accessed because it is deleted. But why is the copy constructor being used at all in this case?
Now I am returning this class via lvalue like this:
MyClass func()
{
return MyClass();
}
No, the returned expression is an xvalue (a kind of rvalue), used to initialise the result for return-by-value (things are a little more complicated since C++17, but this is still the gist of it; besides, you're on C++11).
In this case the move constructor gets called when returning the class object and everything works as expected.
Indeed; an rvalue will initialise an rvalue reference and thus the whole thing can match move constructors.
When I change the code above:
… now the expression is MyClass() << 5, which has type MyClass&. This is never an rvalue. It's an lvalue. It's an expression that refers to an existing object.
So, without an explicit std::move, that'll be used to copy-initialise the result. And, since your copy constructor is deleted, that can't work.
I'm surprised the example compiles at all, since a temporary can't be used to initialise an lvalue reference (your operator's first argument), though some toolchains (MSVS) are known to accept this as an extension.
then would return std::move(MyClass() << 5); work?
Yes, I believe so.
However that is very strange to look at, and makes the reader double-check to ensure there are no dangling references. This suggests there's a better way to accomplish this that results in clearer code:
MyClass func()
{
MyClass m;
m << 5;
return m;
}
Now you're still getting a move (because that's a special rule when returning local variables) without any strange antics. And, as a bonus, the << call is completely standard-compliant.
Your operator return by MyClass&. So you are returning an lvalue, not an rvalue that can be moved automatically.
You can avoid the copy by relying on the standard guarantees regarding NRVO.
MyClass func()
{
MyClass m;
m << 5;
return m;
}
This will either elide the object entirely, or move it. All on account of it being a function local object.
Another option, seeing as you are trying to call operator<< on an rvalue, is to supply an overload dealing in rvalue references.
MyClass&& operator<<(MyClass&& target, int i) {
target << i; // Reuse the operator you have, here target is an lvalue
return std::move(target);
}
That will make MyClass() << 5 itself well formed (see the other answer for why it isn't), and return an xvalue from which the return object may be constructed. Though such and overload for operator<< is not commonly seen.
Your operator<< takes its first parameter as a non-const reference. You can't bind a non-const reference to a temporary. But MyClass() returns the newly-created instance as a temporary.
Also, while func returns a value, operator<< returns a reference. So what else can it do but make a copy to return?

C++11 rvalue reference vs const reference

This may be obvious but I think it is something difficult to me. Given this:
void test(std::string&&) { }
std::string x{"test"};
test(std::move(x)); // ok
This code calls test() with a rvalue reference as parameter so the program compiles as I expect.
Now look at this:
void other_test(const std::string&) { }
std::string x{"test"};
other_test(std::move(x)); // ok???
And here I'm tilted. Why does this version compile? The std::move returns a && type; why then I don't get an error in the second method where I use const&?
I know that
int&& s = 5;
const int& s = 5;
is valid because in both cases I provide something that has not an lvalue, it has no addresses. Are && and const& equivalent? If no, are there differences?
std::move doesn't actually move anything out of it's own. It's just a fancy name for a cast to a T&&. Calling test like this test(std::move(x)); only shows that a T&& is implicitly convertible to a const T&. The compiler sees that test only accepts const T& so it converts the T&& returned from std::move to a const T&, that's all there is to it.
In simple terms:
&& can bind to non-const rvalues (prvalues and xvalues)
const && can bind to rvalues (const and non-const)
& can bind to non-const lvalues
const & can bind to rvalues (prvalues and xvalues) and lvalues (const and non-const for each). A.k.a. to anything.
If you want a function to expressly allow const-Lvalue objects, but expressly disallow Rvalue objects, write the function signature like this:
void test(const std::string&) { }
void test(std::string&&) = delete;//Will now be considered when matching signatures
int main() {
std::string string = "test";
test(string);//OK
//test(std::move(string));//Compile Error!
//test("Test2");//Compile Error!
}
test(std::string&& a) {
something(a) //--> not moved because it has lvalue
Names of variables are lvalues. a is a name of a variable, therefore a is an lvalue expression, and therefore it will not be moved from.
It's unclear what you mean by "has". a is an expression. It is a name of a reference, and references refer to objects. Value categories pertain to expressions, not objects.
test(const std::string& a): a is const lvalue reference and like before I have lvalue and rvalue. And plus more, in this case if I called
std::move(a)
where a is a const& the move works!
If by "works" you mean that it invokes a move constructor or assignment, then no, it does not work because no move construction or assignment has happened.
When you call std::move(x), an rvalue reference to the underlying data, test, will be returned. You are allowed to pass rvalue references as const (and const only!) reference parameters because an rvalue reference is implicitly convertible to a const reference. They are arguably the same thing from the function's point of view (a read only parameter). If you removed the const-qualifier of your parameter, this code would not compile:
void other_test(std::string&) { }
std::string x{"test"};
other_test(std::move(x)); //not okay because
//the function can potentially modify the parameter.
See Bo Qian's youtube video on rvalue vs lvalue.

Is the lifetime of a reference extended?

I'd like to pass a reference into a function. This code does not work, as I'd expect:
struct A {
};
void foo(A& a) {
// do something with a
}
int main(int, char**) {
foo(A());
}
I get the compile error
invalid initialization of non-const reference of type A& from an rvalue of type A
But when I just add the method A& ref() to A like below and call it before passing it along, it seems I can use a. When debugging, the A object is destroyed after foo() is called:
struct A {
A& ref() {
return *this;
}
};
void foo(A& a) {
// do something with a
}
int main(int, char**) {
foo(A().ref());
}
Is this valid code according to the standard? Does calling ref() magically extend the lifetime of the object until foo() returns?
Your code is perfectly valid.
In this line
foo(A().ref());
The instance of a temporary A lives until the end of the statement (;).
That's why it's safe to pass A& returned from ref() to foo (as long as foo doesn't store it).
ref() by itself does not extend any lifetime, but it helps by returning an lvalue reference.
What happens in the case of foo(A()); ? Here the temporary is passed as an rvalue. And in C++ an rvalue does not bind to non-const lvalue references (even in C++11, an rvalue reference does not bind to non-const lvalue references).
From this Visual C++ blog article about rvalue references:
... C++ doesn't want you to accidentally modify temporaries, but directly
calling a non-const member function on a modifiable rvalue is explicit, so
it's allowed ...
A() creates a temporary object of type A. The object exists until the end of the full expression in which it is created. The problem in your original code is not the lifetime of this temporary; it's that the function takes its argument as a non-const reference, and you're not allowed to pass a temporary object as a non-const reference. The simplest change is for foo to take it's argument by const reference, if that's appropriate to what the function does:
void foo(const A&);
int main() {
foo(A());
}
There are several questions in this question. I'll attempt to address all of them:
First, you cannot pass a temporary (prvalue) of type A to a function taking A& because non-const lvalue references cannot bind to rvalues. That's a language restriction. If you want to be able to pass a temporary, you either need to take a parameter of type A&& or of type A const& - the latter since temporaries can bind to const lvalue references.
Is this valid code according to the standard? Does calling ref() magically extend the lifetime of the object until foo() returns?
There is no lifetime extension going on in your program at all. From [class.temp]:
There are three contexts in which temporaries are destroyed at a different point than the end of the full-expression. The first context is when a default constructor is called to initialize an element of an array with
no corresponding initializer (8.6). The second context is when a copy constructor is called to copy an element
of an array while the entire array is copied (5.1.5, 12.8). [...] The third context is when a reference is bound to a temporary.
None of those contexts apply. We are never binding a reference to a temporary in this code. ref() binds *this to an A&, but *this is not a temporary, and then that resulting reference is simply passed into foo().
Consider this variant of your program:
#include <iostream>
struct A {
A& ref() { return *this; }
~A() { std::cout << "~A()\n"; }
};
int main() {
auto& foo = A().ref();
std::cout << "----\n";
}
which prints
~A()
----
illustrating that there is no lifetime extension.
If instead of binding the result of ref() to a reference we instead bound a member:
#include <iostream>
struct A {
A& ref() { return *this; }
int x;
~A() { std::cout << "~A()\n"; }
};
int main() {
auto&& foo = A().x;
std::cout << "----\n";
}
then we actually are binding a temporary to a reference and that third context applies - the lifetime of the complete object of the subobject to which the reference is bound is persisted for the lifetime of the reference. So this code prints:
----
~A()

How does constant reference makes a difference?

I dont understand why the following code doesn't work:
#include <iostream>
using namespace std;
class PayOff{};
class PayOffCall : public PayOff{};
class PayOffBridge{
public:
PayOffBridge( PayOff& innerPayOff){};
};
class VanilaOption{
public:
VanilaOption(PayOffBridge& ThePayOff_){cout << " test " <<endl;}
};
int main () {
PayOffCall thePayOff;
VanilaOption theOption(thePayOff);
return 0;
}
For some reason changing the reference to const in VanilaOption class (code below) makes it work, can someone explain how does it work? The error I get is: no matching function for call to 'VanilaOption::VanilaOption(PayOffCall&)'
but it doesn't help me to figure it out.
class VanilaOption{
public:
VanilaOption(const PayOffBridge& ThePayOff_){cout << " test " <<endl;}
};
I also don't understand why passing PayOffCall reference when PayOffBridge reference is expected works, can someone help me out on this?
Thank you!
The original code doesn't work because thePayOff isn't a value of type PayOffBridge, but rather of type PayOffCall.
The modified code works because it allows the construction of a temporary PayOffBridge object from the PayOff subobject of the PayOffCall object, and then construct the VanillaOption object from that temporary. That is because:
the PayOffBridge constructor is non-explicit,
an lvalue of a derived class can be converted to an lvalue of any of its accessible base classes, and
because temporaries can bind to const lvalue references (but not to non-const lvalue references).
In other words, the const reference version allows code like:
VanilaOption theOption(PayOffBridge(thePayOff));
// ^^^^^^^^^^^^^^^^^^^^^^^-----< temporary
Whereas for the non-const version you'd need a mutable lvalue, perhaps like:
PayOffBridge b(thePayOff);
VanilaOption theOption(b);
// ^----- lvalue
A PayoffCall (like thePayoff variable) is not a PayOffBridge, so it can't be bound to a reference to PayOffBridge.
However, it can be converted to PayOffBridge, via the converting PayOffBridge constructor.
With a reference to const argument such a conversion is performed, producing a temporary that's bound to the formal argument.
A reference to const can in general be bound to a temporary.
An ordinary reference to non-const can't be bound to a temporary.