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);
Related
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).
This question already has answers here:
Why does assignment operator call constructor?
(4 answers)
Closed 2 years ago.
Lets assume I have a program:
class A {
public:
A() { cout << '1'; }
A(int i) { cout << '2'; }
};
int main() {
A a;
a = 0;
return 0;
}
Result of this program will be 12.
So my question is - why am I able to call the second constructor through a = 0, when I already created the object and called the first constructor. I don't understand the whole concept of this. Isn't a constructor supposed to be called only once (during object creation)?
Isn't a constructor supposed to be called only once (during object creation)?
Yes, and that's what happens.
When you do a = 0;, the copy assignment operator A &operator=(const A &) gets called. Since the second operand is an int rather than an A, a new temporary instance of A is constructed, passed to the assignment operator, and then is destroyed right after it finishes.
The A(int) constructor allows implicit conversion from int to A.
So what happens with:
a = 0;
is really:
a = A(0);
If you want to disallow such implicit conversions, you need to make the constructor explicit:
explicit A(int);
This type of constructor is called "conversion constructor".
These type of constructors are used to perform implicit class-type conversions in C++.
Here you can find a little doc if you want to read.
This question already has answers here:
Strange behavior of copy-initialization, doesn't call the copy-constructor!
(6 answers)
Copy constructor not calling
(2 answers)
Closed 2 years ago.
#include <iostream>
class Test {
public:
Test(const int& i)
{
std::cout << "Direct" << std::endl;
}
Test(const Test& t)
{
std::cout << "Copy" << std::endl;
}
};
int main()
{
Test test = 1;
return 0;
}
This program(compiled in C++11) will only output Direct, but the Test test = 1; means implicit convert 1 to a Test and then copy the result to test, I expected it output both of Direct and Copy, could anyone explain it?
Until c++17, this initialization:
Test test = 1;
will create a temporary Test from the int 1, and then the copy constructor is invoked for test. In practice, compilers will do copy-elision, and the temporary will be elided. You can force the compiler to not do the elision by passing the -fno-elide-constructors flag, to see both constructor calls.
From c++17, the wording is changed, and there is simply no temporary on the right hand side, so there is nothing to elide, and only one constructor is called. So even if you use -fno-elide-constructors, you will only see a single constructor call.
This question already has answers here:
What are copy elision and return value optimization?
(5 answers)
Closed 8 years ago.
There is a part of C++ code I don't really understand.
Also I don't know where should I go to search information about it, so I decided to ask a question.
#include <iostream>
#include <string>
using namespace std;
class Test
{
public:
Test();
Test(Test const & src);
Test& operator=(const Test& rhs);
Test test();
int x;
};
Test::Test()
{
cout << "Constructor has been called" << endl;
}
Test::Test(Test const & src)
{
cout << "Copy constructor has been called" << endl;
}
Test& Test::operator=(const Test& rhs)
{
cout << "Assignment operator" << endl;
}
Test Test::test()
{
return Test();
}
int main()
{
Test a;
Test b = a.test();
return 0;
}
Why the input I get is
Constructor has been called
Constructor has been called
?
a.test() creates a new instance by calling "Test()" so that's why the second message is displayed. But why no copy constructor or assignment called?
And if I change "return Test()" to "return *(new Test())" then the copy constructor is called.
So why isn't it called the first time?
Compilers are very smart. Both copies - returning from test and initialising b (not this is not an assignment) - are elided according to the following rule (C++11 ยง12.8):
when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
Compilers are allowed to do this even if it would change the behaviour of your program (like removing your output messages). It's expected that you do not write copy/move constructors and assignment operators that have other side effects.
Note that is only one of four cases in which copy elision can occur (not counting the as-if rule).
The call to a.test() returns by value and this value is then assigned to b "copying" the return value. This invokes the copy constructor.
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).