This question already has answers here:
What are copy elision and return value optimization?
(5 answers)
Copy constructor not called?
(2 answers)
Closed 6 years ago.
As per my understanding, the move constructor will be called when there is a temporary object created. Here the getA() function is returning a temporary object but my program is not printing the message from the move constructor:
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout<<"Hi from default\n";
}
A(A && obj)
{
cout<<"Hi from move\n";
}
};
A getA()
{
A obj;
cout<<"from getA\n";
return obj;
}
int main()
{
A b(getA());
return 0;
}
The compiler is allowed to optimise out the instance obj and send the object directly back to the caller without a conceptual value copy being taken.
This is called named return value optimisation (NRVO). It's a more aggressive optimisation than classical return value optimisation (RVO) that a compiler can invoke to obviate the value copy of an anonymous temporary.
For the avoidance of doubt the compiler can do this even if there is a side-effect in doing so (in your case the lack of console output).
Related
This question already has answers here:
What are copy elision and return value optimization?
(5 answers)
Closed 2 years ago.
I'm trying to understand the following
#include <iostream>
class Number {
int _value;
public:
Number(int number) : _value(number) { std::cout<<"constructed!\n"; }
Number(const Number& other) : _value(other._value) { std::cout<<"copied!\n"; }
~Number() { std::cout<<"destructed!\n"; }
};
Number create_number(int value) {
Number out(value);
return out;
}
int main() {
Number my_number = create_number(10);
}
The output is
constructed!
destructed!
I would have expected that inside create_number an object would be constructed, copied to the output of the function and then destructed once the function returned.
Something like that seems to happen because if I make the copy constructor explicit
explicit Number(const Number& other) : _value(other._value) { std::cout<<"copied!\n"; }
the compiler yells on return out with no matching constructor for initialization of 'Number'. If I call the copy constructor within create_number explicitly or even cast the Number out to a Number (huh?)
return Number(out);
return (Number)out;
the compiler is happy and I get the expected output
constructed!
copied!
destructed!
destructed!
I feel like I'm missing something about return, copy constructors and/or (implicit) casting. Can someone explain this?
That's copy elision. Without getting too much into the C++ standard's obscure language, in most cases the compiler may elide (optimize away) the copy, but isn't required to. This is treated like any other optimization.
Interestingly, this is a rare exception to the as-if rule, where the side-effects of an optimization are observable - see the notes in these links.
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 ]
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);
This question already has answers here:
Why is the copy constructor not called?
(4 answers)
Closed 9 years ago.
I have an understanding that copy constructor will be called when an object is created from an already existing object and also when a function returns object by value. So why in the below code the copy constructor is not called but the default constructor?
class A {
public:
A() { cout << "A" << endl; }
A(A &) { cout << "A&" << endl; }
A(const A &) { cout << "const A&" << endl; }
};
A fun() {
class A a;
return a;
}
int main() {
class A a = fun(); // output: A
}
Short answer: compiler optimizations.
First, the a object from your function is created directly in the scope of the main function, in order to avoid having to copy (or move in C++11) the local parameter out of the scope of the function via the function's return. This is return value optimization.
Then, in main, the statement becomes equivalent to class A a = A() and again the compiler is allowed to the create the a object in place, without copying from a temporary object. This is copy elision.
This is allowed even if the copy constructor (which is bypassed entirely) has side-effects, like in your example.
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).