consider the following:
class X {
public:
X(int i) { cout << "X(int i)" << endl; }
X(const X& x) { cout << "X(const X& x)" << endl; }
};
void main() {
X x1(1);
X x2 = X(1);
X x3 = (X)1;
}
running this code produces this output:
X(int i)
X(int i)
X(int i)
I thought that all of the above three statements are equivalent as the copy CTOR is never called. However, changing X's copy CTOR to be private:
class X {
public:
X(int i) { cout << "X(int i)" << endl; }
private:
X(const X& x) { cout << "X(const X& x)" << endl; }
};
Will fail to compile (In visual studio 2010) with this error:
cannot access private member declared in class 'X'
So it seems the copy CTOR is involved somehow though I don't quite understand how.
Thanks
X x1(1);
X x2 = X(1);
X x3 = (X)1;
The reason is that all of these are not exactly equivalent.
First one is direct-initialization, while the second and third is copy-initialization. For copy-initialization, copy-constructor must be public, or else compiler will give error.
Now the question is, if 2nd and 3rd requires the copy-ctor to be public,then why the following output:
X(int i)
X(int i)
X(int i)
This surely says that copy-ctor is never called which is true. Compiler just elided the call to copy-ctor. According to §8.5/14, in such cases, the compiler is permitted to eliminate the need to call copy-constructor. That is why you don't see copy-ctor being called.
A little inside : in the 2nd and 3rd case, first a temporary is created by calling X(int i), then this temporary was supposed to be passed to the copy-ctor to copy-initialize the object being declared. But the compiler optimizes away this step, eliding the call to copy-ctor.
The X x2 = ... invokes the copy constructor (even if the compiler optimises it out later). Thus, it must still be accessible.
This :
X x3 = (X)1;
is c-style cast from int into the object of type X
This copying :
X x2 = X(1);
is optimized away but the compiler still needs the access to the copy-constructor.
Ist object makes use of parrametrized constructor as you know it and all others are using copy constructor. i.e. why when you are makin it private access violation occurs. The others two objects are making use of copy constructor.
These are the way of using copy constructor.
Related
Here, i am getting different out on different compiler, why is that ?
On msvc compiler, there i'm getting extra destructor statement ?
Why i'm getting this behaviour ?
Am i missing something ?
i had looked many question on stackoverflow, but i can't find anything related to my problem ?
i also tried to look for duplicate, but didn't find one.
class A {
public:
A()
{
std::cout << "A::constructor" << "\n";
}
~A()
{
std::cout << "A::Destructor" << "\n";
}
int x = 0;
int y = 0;
};
class B {
public:
A member_var_1;
int member_var_2;
B()
{
std::cout << "B::constructor" << '\n';
}
B(A a, int b)
{
member_var_1 = a;
member_var_2 = b;
std::cout << "B(A, int)::constructor " << '\n';
}
~B()
{
std::cout << "B::destructor" << '\n';
}
};
int main()
{
B v1 {A(), 5};
}
GCC output:
A::consturctor // parameterized constructor first argument constructor
A::consturctor // construction of B's class member (member_var_1)
B(A, int)::consturcotr // B class parameterized constructor
A::Destructor // Destruction of argument of parameterized constructor
B::destructor // object goes out of scope, so B destructor called
A::Destructor // B's Destructor called member's destructor
MSVC output:
A::consturctor
A::consturctor
B(A, int)::consturcotr
A::Destructor
A::Destructor // what is it destroying? if i define a "class A" copy constructor, then i don't get this output.
B::destructor
A::Destructor
Since you're using C++17 and there is mandatory copy elision from C++17(&onwards), the extra destructor call must not be there.
A msvc bug has been reported as:
MSVC produces extra destructor call even with mandatory copy elision in C++17
Note that if you were to use C++11 or C++14, then it was possible to get an extra destructor call because prior to c++17 there was no mandatory copy elision and the parameter a could've been created using the copy/move constructor which means that you'll get the fourth destructor call as expected. You can confirm this by using the -fno-elide-constructors flag with other compilers. See Demo that has a contrived example of this.
B v1 {A(), 5};
In MSCV compiler, first, the construction of temporary A() is happening then it is elided to to "a" argument of parameterized constructor. that's why the extra destruction is happening for this temporary but it shouldn't be here because it is done implicitly.
So it is a bug in MSCV.
I'm working through chapter 18 of Stroustrup's Principles and Practice and am stuck on one part related to copy constructors.
I have a copy constructor defined as:
X(const X& x) {
out("X(X&)");
val = x.val;
}
X is a struct.
val is just an int value of X.
'out' is:
void out(const string& s) {
cerr << this << "->" << s << ": " << val << "\n";
}
I also have the following 2 functions defined:
X copy(X a) {
return a;
}
and
X copy2(X a) {
X aa = a;
return aa;
}
In main I have:
X loc(4);
X loc2 = loc;
loc2 = copy(loc);
loc2 = copy2(loc);
When I just call copy, the copy constructor is called twice: once for copy's parameter scope and once for the return call. This makes sense to me.
However, when I call copy2, the copy constructor is still just called twice: once for the function argument and once for 'X aa = a.' Why isn't it also called for the return?
There's no guarantee that copy constructors will be called in C++. In the case of return, it's likely to be replaced by a move or completely elided.
See also: What are copy elision and return value optimization?
Since you are returning a local variable, move semantics apply.
Optimizations make copying, moving and returning even more elaborate, see Tatuyuki Ishi's answer.
Here are some good examples for move semantics for return statements.
Simple program:
#include <iostream>
using namespace::std;
class X {
public:
X() {
cout << "Default Constructor called\n";
i = 0;
}
X(int i) {
cout << "Parameterized Constructor called\n";
this->i = i;
}
X(const X& x) {
cout << "Copy Constructor called\n";
i = x.getI();
}
~X() {
cout << "Destructor called\n";
}
int getI() const {
return i;
}
X func() {
cout << "Entered func\n";
X x(2);
return x;
}
private:
int i;
};
int main() {
X x1;
X x2 = x1.func();
cout << "Returned from func\n";
}
It outputs the following:
Default Constructor called
Entered func
Parameterized Constructor called
Copy Constructor called
Destructor called
Returned from func
Destructor called
Destructor called
My issue is that after the 'Returned from func' is printed, no constructor is called when creating the instance x2. I was actually expecting a copy constructor to be called when instantiating x2 as it would have been if we did something like X x2 = x1;
I initially incorrectly assumed that the reason was RVO, so I keep my original answer below.
What your program prints is expected. Note that you print "Returned from function" after you call it and assign the returned value to x2. So the order of prints is correct: it first instantiates x, hence parametrized ctor call, then it copies it to x2, as it exits from the function, then it calls the destructor of x, which is no longer needed, then it actually exits from the function, and only then it reaches your print statement.
Old answer follows
What you observe is Return Value Optimization.
Return value optimization, or simply RVO, is a compiler optimization
technique that involves eliminating the temporary object created to
hold a function's return value.1 In C++, it is particularly notable
for being allowed to change the observable behaviour of the resulting
program.[2]
In general, the C++ standard allows a compiler to perform any
optimization, provided the resulting executable exhibits the same
observable behaviour as if (i.e. pretending) all the requirements of
the standard have been fulfilled. This is commonly referred to as the
"as-if rule".[3] The term return value optimization refers to a
special clause in the C++ standard which goes even further than the
"as-if" rule: an implementation may omit a copy operation resulting
from a return statement, even if the copy constructor has side
effects.[4]
See this wikipedia article, it has an example that is very similar to yours
I'm currently trying to get a hang of move constructor.
I came upon the following (Compiled using g++ d.cpp --std=c++11 -O3)
class A {
string _x;
public:
A(string x) { cout << "default contrsutctor: " << x << "\n"; _x = x; }
A(const A& other) { cout << "copy contrsutctor: " << other._x << "\n"; _x = other._x; }
A(A&& other) { cout << "move contrsutctor: " << other._x << "\n"; _x = other._x; }
A foo() {
cout << "foo: " << _x << "\n";
return A("foo");
}
};
int main()
{
A a;
A b = a;
b.foo();
}
I expect this to output:
default contrsutctor: a
move contrsutctor: a
foo: a
default contrsutctor: foo
However the output is:
default contrsutctor: a
copy contrsutctor: a
foo: a
default contrsutctor: foo
Why isn't the A b = a line optimized to use the move constructor? The a object is never used afterwards, so it would be safe to optimize the code to use it instead of the copy constructor.
I know I could force the move contructor to be invoked with std::move(), but I'd prefer this to happen automatically in cases like this one.
Why isn't the A b = a line optimized to use the move constructor?
What you can do in copy constructor and move constructor could be totally different. The compiler cannot guarantee that the results of the two constructors are identical. Implementing this kind of optimization has the potential of changing the behavior of your program, which breaks the as-if rule.
You need to use std::move to cast a to A&&:
#include <utility>
int main()
{
A a("a");
A b = std::move(a);
b.foo();
}
A correct implementation of the move constructor should be:
A(A&& other)
: _x(std::move(other._x))
{}
After the line A b = std::move(a);, a should be "empty". In this case, a._x will be empty. as pointed by #TonyD in the comments, a._str could be in an unspecified but valid state (move constructor of std:string). You should use a with caution after this line.
A b = a; always invokes the copy constructor, no matter if it could invoke the move constructor. Additionally the lifetime of the object a continues after the assignment, even it is not used anymore.
If you want to use the move constructor, you have to make it explicit:
A b = std::move(a);
Note that this can be dangerous, as a is still accessible after the move. If you accidentally use it later, there may be undefined behavior.
Think about why it should happen automatically. In the example you gave, there is no need, as you can as well use a instead of b. In many cases where it would make more sense move constructor/assignment would be used automatically, e.g. A a; a = foo();.
Why isn't the A b = a line optimized to use the move constructor?
Because that would change the observable behavior of the program. The compiler is not permitted to freely change the observable behavior of the program (§1.9/1), except under very specific circumstances (§12.8/31). This is not one of those circumstances. Remove the side effects from your constructors, and the compiler may optimize them away. Of course, if you remove the side effects, then you won't notice if the compiler optimizes the constructor calls away (unless you examine the assembly or binary output), but that's the whole point.
I have a question about this syntax regarding initialization.
Quoted from http://en.wikipedia.org/wiki/Copy_constructor
X a = X();
// valid given X(const X& copy_from_me) but not valid given X(X& copy_from_me)
// because the second wants a non-const X&
// to create a, the compiler first creates a temporary by invoking the default constructor
// of X, then uses the copy constructor to initialize a as a copy of that temporary.
// However, for some compilers both the first and the second actually work.
#include <iostream>
class Foo
{
public:
Foo()
{
std::cout << "Default Constructor called" << std::endl;
}
Foo(const Foo& other)
{
std::cout << "Copy constructor called" << std::endl;
}
Foo& operator=(const Foo& rhs)
{
std::cout << "Assignment operator called" << std::endl;
}
};
int main()
{
Foo b = Foo(); //case 1:default
Foo c = Foo(a); //case 2: copy constructor
}
Case 1:
Upon changing the parameter from const to non const in the copy constructor, case 1 won't compile as expected from wikipedia. However, when ran using the proper copy constructor, it only calls the default constructor. Why doesn't it also call the copy constructor? Is this an optimization done at compile-time?
Case 2:
The answer to case 1 will probably answer case 2 for me, but why does this only call the copy constructor once?
Foo b = Foo();
This form requires a valid matching copy constructor to exist, but the copy may be optimized away. The fact that it may be optimized away does not relax the requirement that the constructor exist though.
By making your copy constructor take a non-const reference, it no longer matches, since Foo() generates a temporary, and temporaries cannot bind to non-const references. When you make the parameter const reference(or scrap your copy c-tor and use the compiler generated copy c-tor), then it works, because temporaries can bind to const references.
X() is a temporary, so you can't bind it to a non-const reference (although MSVS has an extension that allows it).
1) Yes, it's a compiler optimization
2) Illegal, because a doesn't exist. But in principle, again, yes, a compiler optimization.