C++ const rvalue reference [duplicate] - c++

This question already has answers here:
Why can we use `std::move` on a `const` object?
(6 answers)
Why does calling std::move on a const object call the copy constructor when passed to another object?
(2 answers)
Closed 3 months ago.
Just when I thought I was clear about c++ rvalue references, I came across this code:
class my_class {
public:
my_class() { }
auto set(const std::string& s)
{
std::cout << "set(const std::string&) - copying unmutable lvalue" << std::endl;
m_s = s;
}
auto set(std::string& s)
{
std::cout << "set(std::string& s) - copying mutable lvalue" << std::endl;
m_s = s;
}
auto set(std::string&& s)
{
std::cout << "set(std::string&& s) - moving mutable rvalue" << std::endl;
m_s = std::move(s);
}
auto set(const std::string&& s)
{
std::cout << "set(const std::string&& s) - moving unmutable rvalue" << std::endl;
m_s = std::move(s);
}
private:
std::string m_s;
};
int main()
{
my_class c;
std::string lvalue{"lvalue"};
const std::string const_lvalue{"const lvalue"};
std::cout << "1. c.set(lvalue)" << std::endl;
c.set(lvalue);
std::cout << "2. c.set(std::move(lvalue))" << std::endl;
c.set(std::move(lvalue));
std::cout << "3. c.set(const_lvalue)" << std::endl;
c.set(const_lvalue);
std::cout << "4. c.set(std::move(const_lvalue))" << std::endl;
c.set(std::move(const_lvalue));
std::cout << "5. c.set(std::string{\"rvalue\"})" << std::endl;
c.set(std::string{"rvalue"});
// compile error
// c.set(const std::string{"const rvalue"});
return 0;
}
Output:
1. c.set(lvalue)
set(std::string& s) - copying mutable lvalue
2. c.set(std::move(lvalue))
set(std::string&& s) - moving mutable rvalue
3. c.set(const_lvalue)
set(const std::string&) - copying unmutable lvalue
4. c.set(std::move(const_lvalue))
set(const std::string&& s) - moving unmutable rvalue
5. c.set(std::string{"rvalue"})
set(std::string&& s) - moving mutable rvalue
I don't understand 4. Why do we bind to a const rvalue reference and how does c++ allow to invoke std::move() on this thing?

Related

why child class can convert lvalue to rvalue? in c++ perfect forwarding

using namespace std;
class Data {
public:
Data() {}
~Data() {}
Data(const Data& t) { cout << "Copy Constructor" << endl; }
Data(Data&& t) noexcept { cout << "Move Constructor" << endl; }
Data& operator=(const Data& t) {
cout << "Copy =" << endl;
return *this;
}
Data& operator=(Data&& t) noexcept {
cout << "Move =" << endl;
return *this;
}
};
class Data2 : public Data {};
class Test {
public:
void setData(std::shared_ptr<Data>&& d) { data = std::forward<std::shared_ptr<Data>>(d); }
private:
std::shared_ptr<Data> data;
};
int main() {
Test t;
auto d = std::make_shared<Data>();
t.setData(d); // error
t.setData(move(d)); // ok
auto d1 = std::make_shared<Data2>();
t.setData(d1); // why ok????????????
int debug = 0;
}
Why doesn't t.setData(d1) require std::move to convert d1 to an rvalue but t.setData(d) doesn't compile?
t.setData(d1) has to convert d1 to std::shared_ptr<Data>, it does this by copying into a temporary value. Temporary values are already rvalues so don't require an explicit std::move.
Note that as d1 is passed via a temporary conversion setData doesn't alter d1, you still need to use std::move for this to happen so that d1 is moved into the temporary rather than being copied. E.g in:
auto d = std::make_shared<Data>();
t.setData(std::move(d));
std::cout << d.get() << "\n";
auto d1 = std::make_shared<Data2>();
t.setData(d1);
std::cout << d1.get() << "\n";
auto d2 = std::make_shared<Data2>();
t.setData(std::move(d2));
std::cout << d2.get() << "\n";
d and d2 will end up as null pointers but d1 will still point to the object.
Note that in std::forward<std::shared_ptr<Data>>(d) as you know the exact type of d std::forward is unnecessary and you could just use std::move(d) instead.

C++11 move to local const reference: scope [duplicate]

This question already has answers here:
const reference to a temporary object becomes broken after function scope (life time)
(2 answers)
Closed 4 years ago.
For regular local const reference variables, the scope is prolonged. Which is why the following code works as expected:
#include <iostream>
#include <memory>
struct foo
{
foo()
{
std::cout << "foo() #" << (void*)this << std::endl;
}
~foo()
{
std::cout << "~foo() #" << (void*)this << std::endl;
}
};
int main()
{
auto const& f = std::make_shared<foo>();
std::cout << "f = " << f.get() << std::endl;
return 0;
}
// prints:
// foo() #0x55f249c58e80
// f = 0x55f249c58e80
// ~foo() #0x55f249c58e80
It seems though that this does not work as expected when assigning a moved object using std::move():
#include <iostream>
#include <memory>
#include <list>
struct foo
{
foo()
{
std::cout << "foo() #" << (void*)this << std::endl;
}
~foo()
{
std::cout << "~foo() #" << (void*)this << std::endl;
}
};
int main()
{
std::list<std::shared_ptr<foo>> l;
l.push_back(std::make_shared<foo>());
auto const& f = std::move(l.front());
l.clear();
std::cout << "f = " << f.get() << std::endl;
return 0;
}
// prints
// foo() #0x564edb58fe80
// ~foo() #0x564edb58fe80
// f = 0x564edb58fe80
Does std::move() indeed change the scope, or am I dealing with a compiler bug?
Changing the variable from auto const& f to just auto f fixes the problem. If I wrap the move into another function, it also works:
auto const& f = [&]() { return std::move(l.front()); }();
It's almost like std::move() does not share the same semantics as a function call, but rather as if it was just a regular variable assignment:
auto const& f = std::move(l.front());
Let's put aside std::move() and create this function:
struct sometype {};
const sometype &foobar( const sometype &cref ) { return cref; }
and now we use it:
const sometype &ref = foobar( sometype() );
What do you think, will lifetime of temporary be prolongated in this case? No it would not. Lifetime is only prolongated when you assign to a reference directly. When it goes through a function or through static_cast or std::move that prolongation is gone. So you have exactly the same issue with std::move()
struct sometype {
sometype() { std::cout << "ctype()" << std::endl; }
~sometype() { std::cout << "~ctype()" << std::endl; }
};
const sometype &foobar( const sometype &cref ) { return cref; }
int main()
{
const sometype &cref1 = foobar( sometype() );
sometype &&cref2 = std::move( sometype() );
std::cout << "main continues" << std::endl;
}
output:
ctype()
~ctype()
ctype()
~ctype()
main continues
live example
Note: you should not use std::move() on return statement, it does not give you anything.
For your code change. You should remember that std::move() does not move anything. It is done by special assignment operator or constructor (if they provided for a type). So when you write this code:
const type &ref = std::move( something );
there is no constructor nor assignment operator involved and so no moving happens. For actual moving to happen you have to assign it to a variable:
type val = std::move( something );
now moving would happen if possible or copy otherwise.

common practice for member and parameter storage

seeking general guidance on common design practice here in > c++14 world.
Is const std::string& mName the most appropriate type here? it has the least copies, but is it good style?
I feel like a ref implies some other alias that is or could be in unknown state at some point.
Is it best style for one to make the defensive copy? and go const std::string mName instead? or if that is the case take the copy in the parameter const std::string aName and keep the & in the member field?
whats the most common practice for parameter and member storage and what are the factors that push the decision in other ways?
class Machine {
private:
const std::string& mName;
public:
Machine(const std::string& aName) : mName(aName) {
std::cout << &aName << std::endl;
std::cout << &mName << std::endl;
std::cout << std::endl;
}
};
int main() {
std::string x = "Number 1";
std::cout << &x << std::endl;
Machine m(x);
Machine m2("Number 6");
}
EDIT *
This feels better and also eliminate extra copy on temporaries I think??
class Machine {
private:
const std::string mName;
public:
Machine(const std::string&& aName) : mName(aName) {
std::cout << "1:: " << &aName << std::endl;
std::cout << "1:: " << &mName << std::endl;
}
Machine(const std::string& aName) : mName(aName) {
std::cout << "2:: " << &aName << std::endl;
std::cout << "2:: " << &mName << std::endl;
}
const std::string& name() const {
return mName;
}
};
int main() {
std::string x = "Number 1";
std::cout << &x << std::endl;
Machine m(x);
Machine m2("Number 6");
}
If you're going to store the constructor parameter in your object, then take the object by value. If the caller passes an rvalue, it will be move constructed in. If the user passes an lvalue, you were going to have to create a copy when you copy it into your object, anyhow, so just do it a step earlier.
This results in the cleanest, easiest to understand code:
class Machine {
private:
const std::string mName;
public:
Machine(const std::string aName) : mName(std::move(aName)) {
std::cout << "1:: " << &aName << std::endl; // this has been moved out of...
std::cout << "1:: " << &mName << std::endl;
}
const std::string& name() const {
return mName;
}
};
int main() {
Machine m(function_that_returns_a_string()); // move constructed all the way in - no copies made
string s;
Machine m2(s); // only one copy is made - when calling constructor, but it's then move constructed into the object
}
There are times when objects are still too expensive to move construct, at which point making two versions taking an rvalue and lvalue reference will be a win, but for most cases, the style above is the best approach.

Does this const reference have its life preserved?

I have found this answer to the question "Does a const reference prolong the life of a temporary?", which states:
Only local const references prolong the lifespan.
I'm afraid my standardese is not up to scratch to know whether foo, below, is a local const reference or not.
Does my const std::string& foo below prolong the lifetime of the temporary std::string function argument created in the call to get_or, or do I have a dangling reference?
#include <iostream>
#include <boost/optional.hpp>
struct Foo
{
const std::string& get_or(const std::string& def)
{
return str ? str.get() : def;
}
boost::optional<std::string> str;
};
int main()
{
Foo f;
const std::string& foo = f.get_or("hello world");
std::cout << foo << '\n';
}
const& won't extend lifetimes in that situation. Consider the example here that constructs a temporary and then attempts to print it: it's using the same constructs as your code, but I've altered it to make object construction and destruction more explicit to the user.
#include <iostream>
struct reporting {
reporting() { std::cout << "Constructed" << std::endl;}
~reporting() { std::cout << "Destructed" << std::endl;}
reporting(reporting const&) { std::cout << "Copy-Constructed" << std::endl;}
reporting(reporting &&) { std::cout << "Move-Constructed" << std::endl;}
reporting & operator=(reporting const&) { std::cout << "Copy-Assigned" << std::endl; return *this;}
reporting & operator=(reporting &&) { std::cout << "Move-Assigned" << std::endl; return *this;}
void print() const {std::cout << "Printing." << std::endl;}
};
const reporting& get_or(const reporting& def)
{
return def;
}
int main()
{
const reporting& foo = get_or(reporting{});
foo.print();
return 0;
}
Output:
Constructed
Destructed
printing.
Note how the object is destroyed before printing. is displayed.
You might be wondering why the code still completes with no visible errors: it's the result of Undefined Behavior. The object in question doesn't exist, but because it doesn't depend on state to invoke its method, the program happens to not crash. Other, more complicated examples should carry no guarantee that this will work without crashing or causing other, unexpected behavior.
Incidentally, things are a little different if the temporary is bound directly to the const&:
#include <iostream>
struct reporting {
reporting() { std::cout << "Constructed" << std::endl;}
~reporting() { std::cout << "Destructed" << std::endl;}
reporting(reporting const&) { std::cout << "Copy-Constructed" << std::endl;}
reporting(reporting &&) { std::cout << "Move-Constructed" << std::endl;}
reporting & operator=(reporting const&) { std::cout << "Copy-Assigned" << std::endl; return *this;}
reporting & operator=(reporting &&) { std::cout << "Move-Assigned" << std::endl; return *this;}
void print() const {std::cout << "printing." << std::endl;}
};
const reporting& get_or(const reporting& def)
{
return def;
}
int main()
{
const reporting& foo = reporting{};
foo.print();
return 0;
}
Output:
Constructed
printing.
Destructed
See how the object isn't destroyed until after it is used. In this situation, the object survives until the end of scope.
You passed the string through too many references.
Binding the temporary string to the def parameter of get_or extends the lifetime of the string to the end of the full expression containing the function call, but binding def to the return value of get_or and binding the return value of get_or to foo do not extend the lifetime further. The string is dead by the time you try to print it.
The "temporary" in question is the std::string-object created when calling get_or with a parameter of type const char*. The lifetime of this temporary object is limited with the end of function get_or, and the fact that you return a reference to this temporary and assign it afterwards does not prolong the lifetime. See the following code which uses a simple "custom" string class, which couts construction and destruction:
class MyString {
public:
MyString (const char* str) {
m_str = strdup(str);
cout << "constructor MyString - '" << m_str << "'" << endl;
}
~MyString() {
cout << "destructor MyString - '" << m_str << "'" << endl;
free(m_str);
}
char *m_str;
};
struct Foo
{
const MyString& get_or(const MyString& def)
{
cout << "Foo::get_or with '" << def.m_str << "'" << endl;
return def;
}
};
int main()
{
Foo f;
const MyString& foo = f.get_or("hello world");
cout << "usage of foo?" << endl;
}
Output:
constructor MyString - 'hello world'
Foo::get_or with 'hello world'
destructor MyString - 'hello world'
usage of foo?
Note that the destructor is called before you will have the chance to use foo.
The situation is different if you assign a reference to a temporary directly. Again, the lifetime is until the end of the function main, but it will be used in main and not in any function calling main:
const MyString& foo2 = MyString("hello world2");
cout << "usage of foo..." << endl;
Then the output will be:
constructor MyString - 'hello world2'
usage of foo...
destructor MyString - 'hello world2'

const value and RVO

Say I have this function:
template <class A>
inline A f()
{
A const r(/* a very complex and expensive construction */);
return r;
}
Is it a good idea to declare r const, since a const variable cannot be moved? Note that the returned value is not const. The qualm I am grappling is, that r truly is const, but it may not be a good idea to declare it as such. Yet the qualifier should be helping the compiler generate better code.
As demonstrated here, NRVO elides the copy of r implied by the line return r;
#include <iostream>
struct A {
const char* name;
A( const char* name_ ):name(name_) { std::cout << "created " << name << "\n"; }
A(A const&){ std::cout << "copied " << name << "\n"; }
A(A &&){ std::cout << "moved " << name << "\n"; }
};
A f() {
std::cout << "start of f()\n";
A const r("bob");
std::cout << "body of f()\n";
return r;
}
int main() {
A x = f();
}
And the copy in main is also elided.
If you block NRVO and RVO in some other way (for instance using the flag -fno-elide-constructors when compiling with GCC), the const can cause your object to be copied instead of moved. You can see this if we remove the copy constructor from A:
#include <iostream>
struct A {
const char* name;
A( const char* name_ ):name(name_) { std::cout << "created " << name << "\n"; }
//A(A const&){ std::cout << "copied " << name << "\n"; }
A(A &&){ std::cout << "moved " << name << "\n"; }
};
A f() {
std::cout << "start of f()\n";
A const r("bob");
std::cout << "body of f()\n";
return r;
}
int main() {
A x = f();
}
the code no longer compiles. While the copy constructor isn't executed so long as NRVO occurs, its existence is required by your const local variable.
Now, NRVO requires a few things, such as a single variable which is returned along every single execution path of the function in question: if you ever "abort" and do a return A(), NRVO is blocked, and your const local variable suddenly forces a copy at all return sites.
If class A is under your control, and you want to return const objects by move, you can do
mutable bool resources_were_stolen = false;
and set that to true in a const move constructor
A(const A&& other) { ...; other.resources_were_stolen = true; }
~A() { if (!resources_were_stolen) ... }
Actually, the destructor probably would become if (resources_were_stolen) some_unique_ptr.release();, using the fact that objects lose their const-ness during construction and destruction.