class MyClass
{
public:
int a;
MyClass(int r): a(r) {}
MyClass(const MyClass& ref)
{
cout << "Copy Constructor\n";
a= ref.a;
}
};
int main()
{
MyClass obj(5);
MyClass obj1(MyClass(5)); //Case 1
MyClass obj2(obj); //Case 2
return 0;
}
Why the copy constructor is invoked in Case 2, not in Case 1.
In case 1 a temporary object is passed as argument.
In MyClass obj1(MyClass(5)); the compiler elides the temporary object MyClass(5) because it is allowed to.
In particular, C++ standard 2014 §12.8 para 31, defines cases when copy elision can be performed:
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects... This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which
may be combined to eliminate multiple copies):
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.
Related
This question already has answers here:
What are copy elision and return value optimization?
(5 answers)
Closed 4 years ago.
Today I encountered something I don't really understand about copy constructor.
Consider the next code:
#include <iostream>
using namespace std;
class some_class {
public:
some_class() {
}
some_class(const some_class&) {
cout << "copy!" << endl;
}
some_class call() {
cout << "is called" << endl;
return *this; // <-- should call the copy constructor
}
};
some_class create() {
return some_class();
}
static some_class origin;
static some_class copy = origin; // <-- should call the copy constructor
int main(void)
{
return 0;
}
then the copy constructor is called when assigning origin to copy, which makes sense. However, if I change the declaration of copy into
static some_class copy = some_class();
it isn't called. Even when I am using the create() function, it does not call the copy constructor.
However, when changing it to
static some_class copy = some_class().call();
it does call the copy constructor.
Some research explained that the compiler is allowed to optimize the copy-constructor out, which sound like a good thing. Until the copy-constructor is non-default, as then it might, or might not do something obvious, right?
So when is it allowed for the compiler to optimize out the copy-constructor?
Since C++17, this kind of copy elision is guaranteed, the compiler is not just allowed, but required to omit the copy (or move) construction, even if the copy (or move) constructor has the observable side-effects.
Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible, as the language rules ensure that no copy/move operation takes place, even conceptually:
In the initialization of a variable, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the
variable type:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type:
T f() {
return T();
}
f(); // only one call to default constructor of T
Your code snippet matches these two cases exactly.
Before C++17, the compiler is not required, but allowed to omit the copy (or move) construction, even if the copy/move constructor has observable side-effects. Note that it's an optimization, even copy elision takes place the copy (or move) constructor still must be present and accessible.
On the other hand, call() doesn't match any conditions of copy elision; it's returning *this, which is neither a prvalue nor a object with automatic storage duration, copy construction is required and can't be omitted.
Why this:
struct A
{
A(int) {
cout << "construct from int" << endl;
}
A(A&&) = delete;
A(const A &) {
cout << "copy constructor" << endl;
}
};
int main(){
A a = 0;
}
gives me an error:
error: use of deleted function ‘A::A(A&&)’
And why when I add such move constructor
A(A&&) {
cout << "move constructor" << endl;
}
it compiles fine, but programm's output is just
construct from int
So as far as I understand, compiler asks for constructor but doesn't use it. Why? This makes no sense to me.
P.S. I assume that
A a = 0;
is equvalent of
A a = A(0);
but why neither move constructor nor move assignment operator is called?
According to the C++ Standard (12.8 Copying and moving class objects)
31 When certain criteria are met, an implementation is allowed to omit
the copy/move construction of a class object, even if the constructor
selected for the copy/move operation and/or the destructor for the
object have side effects. In such cases, the implementation treats the
source and target of the omitted copy/move operation as simply two
different ways of referring to the same object, and the destruction of
that object occurs at the later of the times when the two objects
would have been destroyed without the optimization.122 This elision of
copy/move operations, called copy elision, is permitted in the
following circumstances (which may be combined to eliminate multiple
copies):
....
— 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
and
30 A program is ill-formed if the copy/move constructor or the
copy/move assignment operator for an object is implicitly odr-used and
the special member function is not accessible (Clause 11). [ Note:
Copying/moving one object into another using the copy/move constructor
or the copy/move assignment operator does not change the layout or
size of either object. —end note ]
#include<iostream>
#include<string>
using namespace std;
class B;
class A {
public:
A(const A&) { //copy ctor
cout << "Copy ctor A\n";
}
A() {} //default ctor
A(const B&) { //lets call this conversion ctor
cout << "B to A conversion ctor\n";
}
};
class B {
public:
};
int main()
{
B b;
A a = b;
}
The above code prints
B to A conversion ctor.
But as per what I have discovered after looking around for a while, it should print
B to A conversion ctor
Copy ctor A
As first a temporary object of type A is created by conversion ctor and then that object is copied into a wherein copy ctor gets called. Also, when copy ctor is made private, statement A a = b; generates this error:
‘A::A(const A&)’ is private
which is obvious as to copy the temporary object into a copy ctor must be visible.
So my question is why copy ctor is not being eventually called as evident from the output(please correct if am leading wrong somewhere here) even though it is required to be accessible?
A a = b;
This form is called copy-initialization. The applicable rule states that in this case a temporary A object will be constructed from the B instance and that temporary will then be used to direct-initialize a.
However, the compiler is allowed to elide the copy as it is not necessary. Even though the elision is allowed, the class still needs to be copyable for the form to be valid.
You can see the result without elision by passing -fno-elide-constructors to GCC or Clang.
why copy ctor is not being eventually called as evident from the output
According to the standard, an implementation is allowed to omit the copy/move construction in certain criteria. In this case, a temporary A(constructed from b, and will be copied to a) 's construction is omitted.
$12.8/31 Copying and moving class objects [class.copy]
When certain criteria are met, an implementation is allowed to omit
the copy/move construction of a class object, even if the constructor
selected for the copy/move operation and/or the destructor for the
object have side effects.
And
(31.3) — 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
type (ignoring cv-qualification), the copy/move operation can be
omitted by constructing the temporary object directly into the target
of the omitted copy/move
My copy constructor is not being called and I'm not sure why. Here's my code:
template <typename T>
class SmartPtr
{
public:
explicit SmartPtr(T *p) : m_p(p) { cout << "ctor" << endl; }
SmartPtr(const SmartPtr& p) : m_p(p.m_p) { cout << "copy ctor" << endl;}
private:
T* m_p;
};
int main()
{
SmartPtr<int> pt4 = SmartPtr<int>(new int);
}
The output is only "ctor". It looks like a default copy constructor is used. If I add "explicit" then it doesn't compile, giving the error:
"error: no matching function for call to ‘SmartPtr<int>::SmartPtr(SmartPtr<int>)’"
What am I doing wrong here?
This is what is known as Copy Elision. It's a nice optimization where a copy clearly isn't necessary. Instead of effectively running the code:
SmartPtr<int> __tmp(new int);
SmartPtr<int> ptr4(__tmp);
__tmp.~SmartPtr<int>();
The compiler can know that __tmp only exists to construct ptr4, and thus is allowed to construct __tmp in-place in the memory owned by ptr4 as if the actual code originally run was just:
SmartPtr<int> ptr4(new int);
Note that you can tell the compiler NOT to do this too. For instance, on gcc, you can pass the -fno-elide-constructors option and with that single change (additionally logging the destructor), now your code prints:
ctor
copy ctor // not elided!
dtor
dtor // extra SmartPtr!
See demo.
In the standard, §12.8:
This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
In a return statement in a function with a class return type, when ...
In a throw-expression, when ...
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
when the exception-declaration of an exception handler (Clause 15) ...
[Example:
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
Thing t;
return t;
}
Thing t2 = f();
Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class Thing:
the copying of the local automatic object t into the temporary object for the return value of function f()
and the copying of that temporary object into object t2. Effectively, the construction of the local object t
can be viewed as directly initializing the global object t2, and that object’s destruction will occur at program
exit. Adding a move constructor to Thing has the same effect, but it is the move construction from the
temporary object to t2 that is elided. —end example ]
If I write
T t = T();
T is a class.
I think this is calling T's default constructor and then the copy assignment operator.
But the compiler is allowed to get rid of the assignment.
I'm trying to find the description of this behavior written in the C++ standard, but I can't find it.
Could you point me to the right spot in the standard?
I'm asking this because I'm being asked to replace this :
T t;
with
T t = T();
because of a coding rule checking program.
and it happens that the T class is noncopyable and has a private copy constructor and copy assignment operator...
So I'd like to see that the compiler is effectively always getting rid of the copy in this case.
edit:
I have been mislead by something weird:
the noncompyable class was actually inheriting from boost::noncopyable
in this case it does compile.
But if I declare the copy constructor and copy assignment operator private, it does not compile.
exemple. This compiles :
class BA
{
protected:
BA() {}
~BA() {}
private:
BA( const BA& );
const BA& operator=( const BA& );
};
class A : BA
{
};
int main( void )
{
A a = A();
return 0;
}
and the following does not :
class A
{
public:
A() {}
~A() {}
private:
A( const A& );
const A& operator=( const A& );
};
int main( void )
{
A a = A();
return 0;
}
It's no-arg constructing a temporary and then copy constructing it to t, not copy assigning it (8.5/14).
The copy ctor can be elided, but must be accessible anyway (12.8/14-15)
Since you asked for the C++ standard citation, here it is:
12.8 copying class objects
15
*When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor and/or destructor for the object have side effects. In such cases, the implemen-tation treats the source and target of the omitted copy operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects
would have been destroyed without the optimization.111)This elision of copy operations is permitted in the following circumstances (which may be combined to eliminate multiple copies):
— in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type, the copy operation can be omitted by constructing the automatic object directly into the function’s return value.
— when a temporary class object that has not been bound to a reference (12.2) would be copied to a class object with the same cv-unqualified type, the copy operation can be omitted by constructing the temporary object directly into the target of the omitted copy*
Example:
class Thing
{
public:
Thing();
˜Thing();
Thing(const Thing&);
};
Thing f()
{
Thing t;
return t;
}
Thing t2 = f();
Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class Thing:
the copying of the local automatic object t into the temporary object for the return value of function f() and the copying of that temporary object into object t2. Effectively, the construction of the local object t can be viewed as directly initializing the global object t2, and that object’s destruction will occur at program exit.
This is called Return Value Optimization. This article also mentions the relevant C++ standard paragraphs in the footnotes.
You could always be more explicit in your handling of the object if you want to control the behavior more precisely, like allocating the temporary object on the stack yourself via alloca and then calling the placement new operator.
In C++0x, you can replace T t; with T t{};.