In the line commented by ***, why is Bar's copy constructor called? input_bar is a rvalue reference, so I expect the move constructor to be called. Did it convert to an lvalue reference? I can make the move constructor call if I change that line to bar_(std::move(input_bar)).
#include <iostream>
#include <array>
#include <memory>
class Bar
{
public:
Bar(const Bar& bar)
{
std::cout << "copy constructor called" << std::endl;
}
Bar(Bar&& bar)
{
std::cout << "move constructor called" << std::endl;
}
};
class Foo
{
public:
Foo(Bar&& input_bar) :
bar_(input_bar) // ***
{
}
Bar bar_;
};
int main()
{
Bar bar;
Foo foo(std::move(bar));
return 0;
}
Once an entity has a name, it is clearly an lvalue! If you have a name for an rvalue reference, the entity with the name is not an rvalue but an lvalue. The entire point is that you know that this entity references an rvalue and you can legitimately move its content.
If you want to just forward the rvalueness to the next function you call, you'd use std::move(), e.g.:
Foo(Bar&& bar): bar_(std::move(bar)) {}
Without the std::move() the rvalue is considered to be owned by the constructor. With the std::move() it releases the ownership and passes it on to the next function.
You have to move rhrs:
Foo(Bar&& input_bar) :
bar_(std::move(input_bar)) // ***
{
}
The reasoning is that once we actually use the RHR, it should be semantially treated as out of scope. Forcing you to use std::move allows the following code to not be undefined:
Foo(Bar&& input_bar) {
std::cout << input_bar.baz << std::endl;//not undefined
bar_ = Bar{std::move(input_bar)};//input_bar is now essentailly destroyted
//std::cout << input_bar.baz << std::endl;//Undefined behavior, don't do this
}
The general rule of thumb is that only things without names can actually be used as RHR's...RHR as arguments have names, and thus will be treated as LHR untill you call a function on them.
Related
I have this simple piece of code in C++17 and I was expecting that the move constructor was called (or the copy constructor, if I was doing something wrong), while it is just calling the normal constructor and I cannot why is doing this optimization.
I am compiling with -O0 option.
#include <iostream>
using namespace std;
struct Foo {
int m_x;
Foo(int x) : m_x(x) { cout << "Ctor" << endl; }
Foo(const Foo &other) : m_x(other.m_x) { cout << "Copy ctor" << endl; }
Foo(Foo &&other) : m_x(other.m_x) {
other.m_x = 0;
cout << "Move ctor" << endl;
}
};
void drop(Foo &&foo) {}
int main() { drop(Foo(5)); }
I cannot why is doing this optimization.
This is not due to any optimization in C++17. Instead this is due to the fact that you're passing an int when you wrote Foo(5). And since you've provided a converting constructor Foo::Foo(int), it will be used to create a Foo object which will then be bound to the rvalue reference parameter of drop.
Note that in C++17, even if we were to make the parameter of drop to be of type Foo instead of Foo&&, then also there will be no call to the move constructor because of mandatory copy elison.
C++11
On the other hand, if you were using C++11 and using the flag -fno-elide-constructors and parameter to drop was of type Foo instead of Foo&& then you could see that a call would be made to the move ctor.
//--------vvv-----------> parameter is of type Foo instead of Foo&&
void drop(Foo foo) {
std::cout<<"drop called"<<std::endl;
}
int main() {
drop(Foo(5)); //in c++11 with -fno-elide-constructors the move ctor will be called
}
The output of the above modified version in C++11 with -fno-elide-constructors is:
Ctor
Move ctor
drop called
Demo
In function main you create a temporary Foo object from integer 5 but you don't move (nor copy) from it anywhere. To actually call your move (or copy) constructor, you have to move- (or copy-) construct another object from your temporary Foo.
E.g., to call Foo's move constructor:
void drop(Foo &&foo) {
// Move-construct tmp from foo.
Foo tmp { std::move(foo) };
}
CPP Core guidelines F45 states:
Don't return a T&&.
I want to create some class, and pass the class through a sequence of functions that modify the class. Then I will either evaluate some members of that class, or store the class in some container, like std::vector.
I am trying to do this in a way that the class is constructed exactly once, then destroyed, or moved and the moved-from copy is destroyed, and later the stored copy is destroyed (when the container that I stored it in is destroyed).
In essence, I want to do this:
// some class
class foo{}
// construct a foo, with some parameters, and modify it somehow
auto f1 = modify_foo(foo(x, y, z));
// modify the foo some more
auto f2 = modify_foo(f1);
// modify the foo some more
auto f3 = modify_foo(f2);
// use some element of modified foo
auto v = f3.getx();
// maybe store the modified foo in a vector or some other container
vector<foo> vf;
vf.emplace_back(f3);
It should be possible to construct the foo exactly once, and move the constructed foo through any number of modifying functions, then destroy the foo exactly once.
In the case of storing the foo in a vector, an additional copy/move and destroy will have to occur.
I can achieve this behavior, but I can't figure out any way to do it without using this signature for the modify functions:
foo&& modify_foo(foo&& in);
Here is test code that seems to do what I want:
#include <iostream>
#include <functional>
#include <vector>
// A SIMPLE CLASS WITH INSTRUMENTED CONSTRUCTORS
class foo {
public:
// default constructor
foo() {
std::cout << "default construct\n";
}
// copy constructor
foo(foo const &in) : x{ in.x } {
std::cout << "copy construct\n";
};
// copy assignment
foo& operator=(foo const& in) {
x = in.x;
std::cout << "copy assignment\n";
}
// move constructor
foo(foo&& in) noexcept : x(std::move(in.x)) {
std::cout << "move constructor\n";
}
// move assignment
foo& operator=(foo&& in) noexcept {
x = std::move(in.x);
std::cout << "move assignment\n";
return *this;
}
// destructor
~foo() {
std::cout << "destructor\n";
}
void inc() {
++x;
}
int getx() { return x; };
private:
int x{ 0 };
};
Now a function that will take foo&&, modify foo, return foo&&:
// A SIMPLE FUNCTION THAT TAKES foo&&, modifies something, returns foo&&
foo&& modify(foo&& in) {
in.inc();
return std::move(in);
}
Now using the class and modify function:
int main(){
// construct a foo, modify it, return it as foo&&
auto&& foo1 = modify(foo());
// modify foo some more and return it
auto&& foo2 = modify(std::move(foo1));
// modify foo some more and return it
auto&& foo3 = modify(std::move(foo2));
// do something with the modified foo:
std::cout << foo3.getx();
}
This will do exactly what I want. It will call the constructor once, correctly print 3, and call the destructor once.
If I do the same thing, except add this:
std::vector<foo> fv;
fv.emplace_back(std::move(foo3));
It will add one move construct, and another destruct when the vector goes out of scope.
This is exactly the behavior I want, and I haven't figured out any other way to get there without returning foo&& from the modifier, and using auto&& for my intermediate variables, and using std::move() on the parameters being passed to the subsequent calls to modify.
This pattern is very useful to me. It is bothering me that I can't resolve this with CPP core guidelines F.45. The guideline does say:
Returning an rvalue reference is fine when the reference to the temporary is being passed "downward" to a callee; Then the temporary is guaranteed to outlive the function call...
Maybe that is what I am doing?
My questions:
Is there anything fundamentally wrong, or undefined, in what I am doing?
When I do auto&&, and hover over the foo1, it will show it as a foo&&. I still have to wrap the foo1 with std::move(foo1) to get the modify function to accept it as foo&&. I find this a little strange. What is the reason for requiring this syntax?
As was correctly pointed out by NathanOliver, attempting to use rvalue ref's was leaving a dangling reference to an object that was being destroyed at the end of the function's life.
The piece of the puzzle that I was missing was to use 'auto&', instead of 'auto' when returning a ref from a function:
// function taking lvalue ref, returning lvalue ref
foo& modify(foo& in) {
in.inc();
return in;
}
{
auto f = foo{}; // constructed here
auto f1 = modify(f); // <-- BAD!!! copy construct occurs here.
auto& f2 = modify(f); // <-- BETTER - no copy here
} // destruct, destruct
If I use auto& to capture the lvalue ref returning from 'modify', no copies are made. Then I get my desired behavior. One construct, one destruct.
{
// construct a foo
foo foo1{};
// modify some number of times
auto& foo2 = modify(std::move(foo1));
auto& foo3 = modify(std::move(foo2));
auto& foo4 = modify(std::move(foo3));
std::cout << foo4.getx();
} // 1 destruct here
With reference to the following code, I do not understand why the move constructor is called both with an lvalue and rvalue. I would expect copy ctor to be printed when i pass an lvalue to the push method.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class my_obj
{
public:
my_obj()
{
cout << "default ctor\n";
}
my_obj(const my_obj& other)
{
cout << "copy ctor\n";
}
my_obj(my_obj&& other)
{
cout << "move ctor\n";
}
};
class test
{
public:
template<typename T>
void push(T&& object)
{
print(forward<T>(object));
}
template<typename T>
void print(T&& a)
{
cout << "move\n";
my_obj temp = forward<T>(a);
}
template<typename T>
void print(T& a)
{
cout << "val\n";
my_obj temp = forward<T>(a);
}
};
int main()
{
my_obj obj;
test f;
f.push(obj); // why is move ctor called here? shouldnt it be copy ctor since not rvalue
cout << "\nPUSHING TEMP\n\n";
f.push(my_obj {});
}
output:
default ctor
val
move ctor
PUSHING TEMP
default ctor
move
move ctor
In here:
template<typename T>
void print(T& a)
{
cout << "val\n";
my_obj temp = forward<T>(a);
}
a isn't a forwarding reference, it's an lvalue reference. The type T isn't a reference type. So forward<T>(a) behaves the same as move(a). The special case for forwarding references is that the template parameter deduces to a reference type, and forwarding with a reference type yields an lvalue.
You just want my_obj temp = a;
The move constructor is called because you are using std::forward without a forwarding reference (don't do that please). Basically, this line is the culprit:
my_obj temp = forward<T>(a);
Here, T is a my_obj. Remember that std::forward is just a glorified cast. In your case, it is equivalent to:
my_obj temp = static_cast<my_obj&&>(a);
So you are casting a to a rvalue reference, and an rvalue has move semantics, so you see the move constructor being called. You are basically getting the behavior of std::move.
Is it ok that the following code
#include <iostream>
struct Foo
{
Foo() { std::cout << "Foo::Foo" << std::endl; }
~Foo() { std::cout << "Foo::~Foo" << std::endl; }
Foo(const Foo&) { std::cout << "Foo::Foo(const Foo&)" << std::endl; }
Foo& operator=(const Foo&) { std::cout << "Foo::operator=(const Foo&)" << std::endl; return *this; }
Foo(Foo&&) { std::cout << "Foo::Foo(Foo&&)" << std::endl; }
Foo& operator=(Foo&&) { std::cout << "Foo::operator=(Foo&&)" << std::endl; return *this; }
};
Foo foo()
{
Foo second;
return second;
}
int main()
{
foo();
}
produces such output:
Foo::Foo
Foo::Foo(Foo&&)
Foo::~Foo
Foo::~Foo
Why does it call move constructor instead of the copy constructor?
The answer is simply because the standard says so. Section 12.8, paragraph 32:
When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the
object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly
parenthesized) id-expression that names an object with automatic storage duration declared in the body or
parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution
to select the constructor for the copy is first performed as if the object were designated by an rvalue.
And that's basically all there is to it. Logically, if the variable has automatic storage duration, and you return it, then immediately after the return, the variable will not exist any more when the local scope cleans up. That is why, even though it is an lvalue, it is quite similar to an rvalue. Because it's still technically an lvalue, this exception has to be made explicitly in the standard, and here we are. Note that this whole situation is specific to returning from a function, and does not include scope ending in general.
second is returned from foo, and, being a local object, is treated as an rvalue (thanks to Nir Friedman for the correct explanation). The returned Foo object is constructed from a temporary, which binds to the move constructor.
Modern compiler should skip the move constructor call and use copy elision (demo).
#include <iostream>
struct Bar
{
int nb_;
Bar(int nb) :nb_(nb){}
~Bar()
{
std::cout << "~Bar" << "\n";
}
};
struct Foo
{
template<class T>
Foo(T&& param) :bar_(std::move(param))
{
std::cout << "Foo" << "\n";
}
~Foo()
{
std::cout << "~Foo" << "\n";
}
Bar&& bar_;
};
int main()
{
{
Foo foo(Bar(1));
}
std::cin.ignore();
}
//Output:
Foo
~Bar
~Foo
Is this program is legal C++? Does bar_ will become a dangling reference once the Foo constructor will be finished?
If this is not legal in term of C++ standard, when is it useful to have a rvalue reference as a field ?
Does bar_ will become a dangling reference once the Foo constructor will be finished?
Not quite. It becomes a dangling reference at the point where the temporary created by Bar(1) is destroyed: at the end of the full-expression Foo foo(Bar(1)).
That also shows a usage example of rvalue reference members, for example forward_as_tuple:
struct woof
{
using my_tuple = std::tuple<std::vector<int>, std::string>;
my_tuple m;
woof(my_tuple x) : m(std::move(x)) {}
};
std::vector<int> v{1,2,3,4,5};
woof w( forward_as_tuple(std::move(v), std::string{"hello world"}) );
forward_as_tuple can store rvalue references (here: in a tuple<vector<int>&&, string&&>), which still can be used by the ctor of woof to move v and the temporary created by std::string{"hello world"}.
Yes, bar_ will become a dangling reference after the constructor has finished.
It rarely makes sense to store a reference, but some cases exist. I don't think there is any difference between the different types of references. The stored reference references another object and you need to make sure that the object is still valid when the reference is used.
Note that if you write
Bar b( 1 );
Foo foo(std::move(b));
foo will not have moved b, it is just storing a reference to it. b is still valid and can be used. Only if you have a member of Foo which actually moves from the stored reference, b will be moved.