Here is an extract from item 56 of the book "C++ Gotchas":
It's not uncommon to see a simple
initialization of a Y object written
any of three different ways, as if
they were equivalent.
Y a( 1066 );
Y b = Y(1066);
Y c = 1066;
In point of fact, all three of these
initializations will probably result
in the same object code being
generated, but they're not equivalent.
The initialization of a is known as a
direct initialization, and it does
precisely what one might expect. The
initialization is accomplished through
a direct invocation of Y::Y(int).
The initializations of b and c are
more complex. In fact, they're too
complex. These are both copy
initializations. In the case of the
initialization of b, we're requesting
the creation of an anonymous temporary
of type Y, initialized with the value
1066. We then use this anonymous temporary as a parameter to the copy
constructor for class Y to initialize
b. Finally, we call the destructor for
the anonymous temporary.
To test this, I did a simple class with a data member (program attached at the end) and the results were surprising. It seems that for the case of c, the object was constructed by the copy constructor rather than as suggested in the book.
Does anybody know if the language standard has changed or is this simply an optimisation feature of the compiler? I was using Visual Studio 2008.
Code sample:
#include <iostream>
class Widget
{
std::string name;
public:
// Constructor
Widget(std::string n) { name=n; std::cout << "Constructing Widget " << this->name << std::endl; }
// Copy constructor
Widget (const Widget& rhs) { std::cout << "Copy constructing Widget from " << rhs.name << std::endl; }
// Assignment operator
Widget& operator=(const Widget& rhs) { std::cout << "Assigning Widget from " << rhs.name << " to " << this->name << std::endl; return *this; }
};
int main(void)
{
// construct
Widget a("a");
// copy construct
Widget b(a);
// construct and assign
Widget c("c");
c = a;
// copy construct!
Widget d = a;
// construct!
Widget e = "e";
// construct and assign
Widget f = Widget("f");
return 0;
}
Output:
Constructing Widget a
Copy constructing Widget from a
Constructing Widget c
Assigning Widget from a to c
Copy constructing Widget from a
Constructing Widget e
Constructing Widget f
Copy constructing Widget from f
I was most surprised by the results of constructing d and e. To be precise, I was expecting an empty object to be created, and then an object to be created and assigned to the empty object. In practice, the objects were created by the copy constructor.
The syntax
X a = b;
where a and b are of type X has always meant copy construction. Whatever variants, such as:
X a = X();
are used, there is no assignment going on, and never has been. Construct and assign would be something like:
X a;
a = X();
The compiler is permitted to optimize cases b and c to be the same as a. Further, copy construction and assignment operator calls can be wholly eliminated by the compiler anyway, so whatever you see isn't necessarily the same with different compilers or even compiler settings.
As of C++17, all three of these are equivalent (unless Y::Y(int) is explicit, which would simply disallow c) because of what is often called mandatory copy elision.
Even Y c = 1066; creates only the one Y object because the result of the implicit conversion to Y is a prvalue that is used to initialize c rather than to create a temporary.
Related
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.
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.
Hey this is a really basic question and but I got confused about it. Say I created an object
MyObject a.
It comes with a copy constructor, so I know I can do this:
MyObject b(a);
But can I do this?
MyObject& b(a);
And if I do this:
MyObject b = a; what is in b? Apology if this question is too fundamental to be bothered posting.
Doing MyObject& b(a) has nothing to do with the copy constructor. It just creates b which is a reference to the object a. Nothing is copied. Think of b as an alias for the object a. You can use b and a equivalently from then on to refer to the same object.
MyObject b = a; will use the copy constructor, just as MyObject b(a); would.
There are two forms of initialisation: T x = a; is known as copy-initialization; T x(a) and T x{a} are known as direct-initialization.
When T is a reference type, it doesn't matter which type of initialisation is used. Both have the same effect.
When T is a class type, we have two possibilities:
If the initialisation is direct-initialization (MyClass b(a);), or, if it is copy-initialization with a being derived from or the same type as T (MyClass b = a;): an applicable constructor of T is chosen to construct the object.
As you can see, both of your examples fall in this category of class type initialisers.
If the initialisation is any other form of copy-initialization, any user-defined conversion sequence will be considered followed by a direct-initialization. A user-defined conversion sequence is basically any sequence of standard conversions with a single conversion constructor thrown in there.
If c were of Foo class type and there was a conversion constructor from Foo to MyClass, then MyClass b = c; would be equivalent to MyClass b(MyClass(c));.
So basically, if the source and destination types are the same, both forms of initialisation are equivalent. If a conversion is required, they are not. A simple example to show this is:
#include <iostream>
struct Bar { };
struct Foo
{
Foo(const Foo& f) { std::cout << "Copy" << std::endl; }
Foo(const Bar& b) { std::cout << "Convert" << std::endl; }
};
int main(int argc, const char* argv[])
{
Bar b;
Foo f1(b);
std::cout << "----" << std::endl;
Foo f2 = b;
return 0;
}
The output for this program (with copy elision disabled) is:
Convert
----
Convert
Copy
Of course, there are lots of other types of initialisations too (list initialisation, character arrays, aggregates, etc.).
Here is my view:
References are tied to someones else's storage, whenever u access a reference, you’re accessing that storage. References cannot be assigned to null because of the fact that they are just an aliases. A reference must be initialized when it is created. (Pointers can be initialized at any time.)
so when you say
MyObject& b(a);
compiler allocates a piece of storage b, and ties the reference to a.
when you say
MyObject b = a;
you pass a reference of a to the copy constructor and creates new b from it. Note that its a deep copy only if you have a copy constructor written for it. Otherwise it calls the default copy constructor which results in a shallow copy.
and when you say
a = b; // after creating these objects
it translates as Object::operator=(const Object&), thus A.operator=(B) is called (invoke simple copy, not copy constructor!)
I do not understand how this code is compiling. Can somebody please explain what is going on in there.
#include <iostream>
using namespace std;
class B
{
public:
B(const char* str = "\0") //default constructor
{
cout << "Constructor called" << endl;
}
B(const B &b) //copy constructor
{
cout << "Copy constructor called" << endl;
}
};
int main()
{
B ob = "copy me"; //why no compilation error.
return 0;
}
The optput is:
Constructor called
P.S.: I could not think of a more apt title than this, Anyone who can think of a better title, please modify it.
The type of "copy me" is char const[8], which decays to char const *. Since the default constructor is not explicit, "copy me" can be implicitly converted to B, and thus ob can be copy-constructed from that implicitly converted, temporary B-object.
Had the default constructor been declared explicit, you would have had to write one of the following:
B ob1 = B("copy me");
B ob2("copy me");
Had the copy constructor also been declared explicit, you would have had to say one of these:
B ob3(B("copy me"));
B ob4("copy me");
In practice, all copies will be elided by any half-decent compiler, and you always end up with a single default constructor invocation.
Because this
B ob = "copy me";
invokes the copy constructor, which takes argument const B &b and you class B has a constructor
B(const char* str = "\0")
which is not defined as explicit.
The compiler is allowed to make one implicit conversion.
So, what happens here:
B ob = "copy me";
is:
Create a temp, unnamed object B, using the provided const char* str - this is allowed, since class B has constructor, which takes one argument and is not defined as explicit. In other words, all objects with type B can be constructed from a const char*
Create the object ob, using the temp object, created in 1..
If you add keyword explicit to your default constructor to prevent implicit conversion. Then it will not compile. Your answer is implicit conversion.
That's because of implicit conversion of the assignment statement to
B ob("copy me");
Try this to fail compilation (look at the explicit keyword):
#include <iostream>
using namespace std;
class B
{
public:
explicit B(const char* str = "\0") //default constructor
{
cout << "Constructor called" << endl;
}
B(const B &b) //copy constructor
{
cout << "Copy constructor called" << endl;
}
};
int main()
{
B ob = "copy me"; //why no compilation error.
return 0;
}
[it shouldn't compile because] the specified line does not match in data types
There is no compilation error because there exists a constructor of B that takes const char* as an argument, allowing for conversion between const char* and B.
This is because of implicit conversion. Add a Explicit to your Default constructor and try. it will Fail to compile.
Edit: after discussion with Kerrek SB I got a copy of the C++11 standard. I was wrong, there's a temporary. I'm editing this reply to reflect my newly acquired understanding.
15 years ago I knew C++ extremely well (that was around the time the first standard was to be released). I haven't used it since late 1998 so I have forgotten a lot and I know almost nothing of the recent standards.
The code is correct.
B ob = "copy me"; //why no compilation error.
There's no error. This is parsed as a declaration of an initialized variable. B is the type, ob the name of the variable, "copy me" the initializer.
The initializer is of type const char* and there is, in class B, a constructor that takes a const char* typed single argument. That constructor is not declared restricted by explicit and therefore can be used in this context.
Either a temporary object is created and then copied (you see both lines printed out) or the copy is elided and only the conversion constructor is called to construct directly at the destination (you see only one line printed). The standard explicitly allows this even if the copy constructor has side effects.
The program execution will print Constructor called to stdout. It may or may not then also print Copy constructor called. There is no error.
This question already has answers here:
Is there a difference between copy initialization and direct initialization?
(9 answers)
Closed 1 year ago.
Simple question: are the following statements equivalent? or is the second one doing more implicit things behind the scenes (if so, what?)
myClass x(3);
myClass x = myClass(3);
Thanks!
They are not completely identical. The first is called "direct initialization" while the second is called "copy initialization".
Now, the Standard makes up two rules. The first is for direct initialization and for copy initialization where the initializer is of the type of the initialized object. The second rule is for copy initialization in other cases.
So, from that point of view both are termed in one - the first - rule. In the case where you have copy initialization with the same type, the compiler is allowed to elide a copy, so it can construct the temporary you create directly into the initialized object. So you can end up very well with the same code generated. But the copy constructor, even if the copy is elided (optimized out), must still be available. I.e if you have a private copy constructor, that code is invalid if the code in which it appears has no access to it.
The second is called copy-initialization, because if the type of the initializer is of a different type, a temporary object is created in trying to implicitly convert the right side to the left side:
myclass c = 3;
The compiler creates a temporary object of the type of myclass then when there is a constructor that takes an int. Then it initializes the object with that temporary. Also in this case, the temporary created can be created directly in the initialized object. You can follow these steps by printing messages in constructors / destructors of your class and using the option -fno-elide-constructors for GCC. It does not try to elide copies then.
On a side-note, that code above has nothing to do with an assignment operator. In both cases, what happens is an initialization.
The second one may or may not call for an extra myclass object construction if copy elision is not implemented by your compiler. However, most constructors, have copy elision turned on by default even without any optimization switch.
Note initialization while construction never ever calls the assignment operator.
Always, keep in mind:
assignment: an already present object gets a new value
initialization: a new object gets a value at the moment it is born.
In the second one, a temporary object is created first and then is copied into the object x using myClass's copy constructor. Hence both are not the same.
I wrote the following to try and illustrate understand what's going on:
#include <iostream>
using namespace std;
class myClass
{
public:
myClass(int x)
{
this -> x = x;
cout << "int constructor called with value x = " << x << endl;
}
myClass(const myClass& mc)
{
cout << "copy constructor called with value = " << mc.x << endl;
x = mc.x;
}
myClass & operator = (const myClass & that)
{
cout << "assignment called" << endl;
if(this != &that)
{
x = that.x;
}
return *this;
}
private:
int x;
};
int main()
{
myClass x(3);
myClass y = myClass(3);
}
When I compile and run this code I get the following output:
$ ./a.out
int constructor called with value x = 3
int constructor called with value x = 3
This would seem to indicate that there is no difference between the two calls made in the main function, but that would be wrong. As litb pointed out, the copy constructor must be available for this code to work, even though it gets elided in this case. To prove that, just move the copy constructor in the code above to the private section of the class definition. You should see the following error:
$ g++ myClass.cpp
myClass.cpp: In function ‘int main()’:
myClass.cpp:27: error: ‘myClass::myClass(const myClass&)’ is private
myClass.cpp:37: error: within this context
Also note that the assignment operator is never called.