Different behaviour based on move constructor [duplicate] - c++

This question already has an answer here:
Does the behavior of guaranteed copy elision depend on existence of user-defined copy constructor?
(1 answer)
Closed 1 year ago.
I have a piece of code to check if move happened.
struct Foo
{
Foo() = default;
Foo(Foo&& other) = default;
double a[1000];
double b[1000];
};
Foo giveMe()
{
Foo ret;
std::cout << &ret << std::endl;
return ret;
}
int main()
{
Foo o1 = giveMe();
std::cout << &o1 << std::endl;
return 0;
}
Well, it does not. The addresses of ret and o1 are, for instance, as follows:
008F7D78
008FBC08
However, after slight change in move constructor:
struct Foo
{
Foo() = default;
Foo(Foo&& other) {}
double a[1000];
double b[1000];
};
the addressees are the same:
010FBE28
010FBE28
What is happening here? I assume that this behaviour is somehow related to POD or trivial types but I am not sure how.

What is happening here?
Optimisation is happpening here. More precisely: Named Return Value Optimisation (NRVO).
Even though the abstract machine moves in both cases, NRVO optimised (elided) the move away from the second example.
The address check is there just to see if move or copy happened.
The class isn't copyable, so a copy can't have happened. Also, the address check cannot distinguish between copied and moved object. Either would have a different address.
To clarify a possible misconception: "Move" construction creates a new object using the move constructor. That new object typically has a different address than the object that was moved from, just like when copying. The only case where the address of the new object is the same is when the move is optimised away such that the constructor is never actually called (which can also be done with copies).

Related

Is it safe to modify RVO values within an RAII construct? [duplicate]

This question already has an answer here:
Clang modifies return value in destructor?
(1 answer)
Closed 3 years ago.
Consider the following program:
#include <functional>
#include <iostream>
class RvoObj {
public:
RvoObj(int x) : x_{x} {}
RvoObj(const RvoObj& obj) : x_{obj.x_} { std::cout << "copied\n"; }
RvoObj(RvoObj&& obj) : x_{obj.x_} { std::cout << "moved\n"; }
int x() const { return x_; }
void set_x(int x) { x_ = x; }
private:
int x_;
};
class Finally {
public:
Finally(std::function<void()> f) : f_{f} {}
~Finally() { f_(); }
private:
std::function<void()> f_;
};
RvoObj BuildRvoObj() {
RvoObj obj{3};
Finally run{[&obj]() { obj.set_x(5); }};
return obj;
}
int main() {
auto obj = BuildRvoObj();
std::cout << obj.x() << '\n';
return 0;
}
Both clang and gcc (demo) output 5 without invoking the copy or move constructors.
Is this behavior well-defined and guaranteed by the C++17 standard?
Copy elision only permits an implementation to remove the presence of the object being generated by a function. That is, it can remove the copy from obj to the return value object of foo and the destructor of obj. However, the implementation can't change anything else.
The copy to the return value would happen before destructors for local objects in the function are called. And the destructor of obj would happen after the destructor of run, because destructors for automatic variables are executed in reverse-order of their construction.
This means that it is safe for run to access obj in its destructor. Whether the object denoted by obj is destroyed after run completes or not does not change this fact.
However, there is one problem. See, return <variable_name>; for a local variable is required to invoke a move operation. In your case, moving from RvoObj is the same as copying from it. So for your specific code, it'll be fine.
But if RvoObj were, for example, unique_ptr<T>, you'd be in trouble. Why? Because the move operation to the return value happens before destructors for local variables are called. So in this case obj will be in the moved-from state, which for unique_ptr means that it's empty.
That's bad.
If the move is elided, then there's no problem. But since elision is not required, there is potentially a problem, since your code will behave differently based on whether elision happens or not. Which is implementation-defined.
So generally speaking, it's best not to have destructors rely on the existence of local variables that you're returning.
The above purely relates to your question about undefined behavior. It isn't UB to do something that changes behavior based on whether elision happens or not. The standard defines that one or the other will happen.
However, you cannot and should not rely upon it.
Short answer: due to NRVO, the output of the program may be either 3 or 5. Both are valid.
For background, see first:
in C++ which happens first, the copy of a return object or local object's destructors?
What are copy elision and return value optimization?
Guideline:
Avoid destructors that modify return values.
For example, when we see the following pattern:
T f() {
T ret;
A a(ret); // or similar
return ret;
}
We need to ask ourselves: does A::~A() modify our return value somehow? If yes, then our program most likely has a bug.
For example:
A type that prints the return value on destruction is fine.
A type that computes the return value on destruction is not fine.
[From https://stackoverflow.com/a/54566080/9305398 ]

How to elide copy when chaining?

I am creating a class of chaining-type, such as the small example below. It seems that when chaining member functions, then the copy constructor is invoked. Is there a way to get rid of the copy constructor call? In my toy example below, it is obvious that I'm only dealing with temporaries and thus there "should" (maybe not by the standards, but logically) be an elision. The second best choice, to copy elision, would be for the move constructor to be called, but this is not the case.
class test_class {
private:
int i = 5;
public:
test_class(int i) : i(i) {}
test_class(const test_class& t) {
i = t.i;
std::cout << "Copy constructor"<< std::endl;
}
test_class(test_class&& t) {
i = t.i;
std::cout << "Move constructor"<< std::endl;
}
auto& increment(){
i++;
return *this;
}
};
int main()
{
//test_class a{7};
//does not call copy constructor
auto b = test_class{7};
//calls copy constructor
auto b2 = test_class{7}.increment();
return 0;
}
Edit: Some clarifications.
1. This does not depend on optimization level.
2. In my real code, I have more complex (e.g. heap allocated) objects than ints
Partial answer (it doesn't construct b2 in place, but turns the copy construction into a move construction): You can overload the increment member function on the value category of the associated instance:
auto& increment() & {
i++;
return *this;
}
auto&& increment() && {
i++;
return std::move(*this);
}
This causes
auto b2 = test_class{7}.increment();
to move-construct b2 because test_class{7} is a temporary, and the && overload of test_class::increment is called.
For a true in-place construction (i.e. not even a move construction), you can turn all special and non-special member functions into constexpr versions. Then, you can do
constexpr auto b2 = test_class{7}.increment();
and you neither a move nor a copy construction to pay for. This is, obviously, possible for the simple test_class, but not for a more general scenario that doesn't allow for constexpr member functions.
Basically, assigning a reference to a value requires invoking a constructor, i.e. a copy or a move. This is different from copy-elision where it is known on both sides of the function to be the same distinct object. Also a reference can refer to a shared object much like a pointer.
The simplest way is probably to make the copy constructor being fully optimized away. The value setting is already optimized by the compiler, it is just the std::cout that cannot be optimized away.
test_class(const test_class& t) = default;
(or just remove both the copy and move constructor)
live example
Since your issue is basicly with the reference, a solution is probably not returning a reference to the object if you want to stop copying in this way.
void increment();
};
auto b = test_class{7};//does not call copy constructor
b.increment();//does not call copy constructor
A third method is just relying on copy elision in the first place - however this requires a rewrite or encapsulation of the operation into one function and thus avoiding the issue altogether (I'm aware this may not be what you want, but could be a solution to other users):
auto b2 = []{test_class tmp{7}; tmp.increment().increment().increment(); return tmp;}(); //<-- b2 becomes 10 - copy constructor not called
A fourth method is using a move instead, either invoked explicit
auto b2 = std::move(test_class{7}.increment());
or as seen in this answer.

Why does my rvalue copy constructor not work? [duplicate]

This question already has answers here:
What are copy elision and return value optimization?
(5 answers)
Closed 5 years ago.
I was testing my knowledge in C++ and have encountered a problem. Consider this code:
class A
{
public:
A(int n = 0)
: m_n(n)
{
std::cout << 'd';
}
A(const A& a)
: m_n(a.m_n)
{
std::cout << 'c';
}
A(A&& a){
std::cout<<'r';
}
private:
int m_n;
};
int main()
{
A a = A();
std::cout << std::endl;
return 0;
}
Clearly, the A() constructor is an rvalue, as no permanent object has been created. So I think that first I have to see "d" as output. Then we are copying the rvalue to our new object, which is yet to be initialised. I have implemented a copy constructor that accepts an rvalue as an argument but I did not see the proof (no "r" output).
Can someone explain why that is?
You're seeing a form of copy elision -- the compiler is actually required to optimize and implement A a = A() by initializing a directly with the arguments to the constructor expression (() in this case) rather than creating a temporary and copying it into a. If you want to enforce an unnamed object to be copied, you need to initialize a with an expression that is not a simple constructor call.
Note that even then, the compiler may elide construtor calls by the as-if rule (if the only visible side effects are in the copy/move constructors/destructors. Your best bet to "force" a call to the move ctor is to use a named value and std::move:
A a1;
A a2 = std::move(a1);

is twice calls to copy constructor happen for this c++ code sample?

for method:
Object test(){
Object str("123");
return str;
}
then, I had two methods to call it:
code 1:
const Object &object=test();
code 2:
Object object=test();
which one is better? is twice calls to copy constructor happen in code 2 if without optimize?
other what's the difference?
for code2 I suppose:
Object tmp=test();
Object object=tmp;
for code1 I suppose:
Object tmp=test();
Object &object=tmp;
but the tmp will be deconstructor after the method.so it must add const?
is code 1 right without any issues?
Let's analyse your function:
Object test()
{
Object temp("123");
return temp;
}
Here you're constructing a local variable named temp and returning it from the function. The return type of test() is Object meaning you're returning by value. Returning local variables by value is a good thing because it allows a special optimization technique called Return Value Optimization (RVO) to take place. What happens is that instead of invoking a call to the copy or move constructor, the compiler will elide that call and directly construct the initializer into the address of the caller. In this case, because temp has a name (is an lvalue), we call it N(amed)RVO.
Assuming optimizations take place, no copy or move has been performed yet. This is how you would call the function from main:
int main()
{
Object obj = test();
}
That first line in main seems to be of particular concern to you because you believe that the temporary will be destroyed by the end of the full expression. I'm assuming it is a cause for concern because you believe obj will not be assigned to a valid object and that initializing it with a reference to const is a way to keep it alive.
You are right about two things:
The temporary will be destroyed at the end of the full expression
Initializing it with a reference to const will extend its life time
But the fact that the temporary will be destroyed is not a cause for concern. Because the initializer is an rvalue, its contents can be moved from.
Object obj = test(); // move is allowed here
Factoring in copy-elision, the compiler will elide the call to the copy or move constructor. Therefore, obj will be initialized "as if" the copy or move constructor was called. So because of these compiler optimizations, we have very little reason to fear multiple copies.
But what if we entertain your other examples? What if instead we had qualified obj as:
Object const& obj = test();
test() returns a prvalue of type Object. This prvalue would normally be destructed at the end of the full expression in which it is contained, but because it is being initialized to a reference to const, its lifetime is extended to that of the reference.
What are the differences between this example and the previous one?:
You cannot modify the state of obj
It inhibits move semantics
The first bullet point is obvious but not the second if you are unfamiliar with move semantics. Because obj is a reference to const, it cannot be moved from and the compiler cannot take advantage of useful optimizations. Assigning reference to const to an rvalue is only helpful in a narrow set of circumstances (as DaBrain has pointed out). It is instead preferable that you exercise value-semantics and create value-typed objects when it makes sense.
Moreover, you don't even need the function test(), you can simply create the object:
Object obj("123");
but if you do need test(), you can take advantage of type deduction and use auto:
auto obj = test();
Your last example deals with an lvalue-reference:
[..] but the tmp will be destructed after the method. So must we add const?
Object &object = tmp;
The destructor of tmp is not called after the method. Taking in to account what I said above, the temporary to which tmp is being initialized will be moved into tmp (or it will be elided). tmp itself doesn't destruct until it goes out of scope. So no, there is no need to use const.
But a reference is good if you want to refer to tmp through some other variable. Otherwise, if you know you will not need tmp afterwards, you can move from it:
Object object = std::move(tmp);
Both your examples are valid - in 1 const reference refers to a temporary object, but lifetime of this object is prolonged till the reference goes out of scope (see http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/). The second example is obviously valid, and most modern compilers will optimize away additional copying (even better if you use C+11 move semantics) so for practical purposes examples are equivalent (though in 2 additionally you can modify the value).
In C++11, std::string has a move constructor / move assignment operator, hence the code:
string str = test();
will (at worst) have one constructor call and one move assignment call.
Even without move semantics, this will (likely) be optimised away by NRVO (return value optimisation).
Don't be afraid of returning by value, basically.
Edit: Just to make it 100% clear what is going on:
#include <iostream>
#include <string>
class object
{
std::string s;
public:
object(const char* c)
: s(c)
{
std::cout << "Constructor\n";
}
~object()
{
std::cout << "Destructor\n";
}
object(const object& rhs)
: s(rhs.s)
{
std::cout << "Copy Constructor\n";
}
object& operator=(const object& rhs)
{
std::cout << "Copy Assignment\n";
s = rhs.s;
return *this;
}
object& operator=(object&& rhs)
{
std::cout << "Move Assignment\n";
s = std::move(rhs.s);
return *this;
}
object(object&& rhs)
: s(std::move(rhs.s))
{
std::cout << "Move Constructor\n";
}
};
object test()
{
object o("123");
return o;
}
int main()
{
object o = test();
//const object& o = test();
}
You can see that there is 1 constructor call and 1 destructor call for each - NRVO kicks in here (as expected) eliding the copy/move.
Code 1 is correct. As I said, the C++ Standard guarantees a temporary to a const reference is valid. It's main usage is polymorphic behavior with refenences:
#include <iostream>
class Base { public: virtual void Do() const { std::cout << "Base"; } };
class Derived : public Base { public: virtual void Do() const { std::cout << "Derived"; } };
Derived Factory() { return Derived(); }
int main(int argc, char **argv)
{
const Base &ref = Factory();
ref.Do();
return 0;
}
This will return "Derived". A famouse example was Andrei Alexandrescu's ScopeGuard but with C++11 it's even simpler yet.

Neither copy nor move constructor called [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why copy constructor is not called in this case?
What are copy elision and return value optimization?
Can anybody explain to me why the following program yields output "cpy: 0" (at least when compiled with g++ 4.5.2):
#include<iostream>
struct A {
bool cpy;
A() : cpy (false) {
}
A (const A & a) : cpy (true) {
}
A (A && a) : cpy (true) {
};
};
A returnA () { return A (); }
int main() {
A a ( returnA () );
std::cerr << "cpy: " << a.cpy << "\n";
}
The question arised when I tried to figure out seemingly strange outcome of this example: move ctor of class with a constant data member or a reference member
The compiler is free to elide copy and move construction, even if these have side effects, for objects it creates on it own behalf. Temporary objects and return values are often directly constructed on the correct location, eliding copying or moving them. For return values you need to be a bit careful to have the elision kick in, though.
If you want to prevent copy elision, you basically need to have two candidate objects conditionally be returned:
bool flag(false);
A f() {
A a;
return flag? A(): a;
}
Assuming you don't change flag this will always create a copy of a (unless compilers got smarter since I last tried).