I have the following class with both a normal constructor and copy constructor defined.
#include <iostream>
class Bla
{
public:
Bla()
{
std::cout << "Normal Constructor Called\n";
}
Bla(const Bla& other)
{
std::cout << "Copy Constructor Called\n";
}
};
int main()
{
Bla a = Bla(); // prints Normal Constructor
}
In the main function, it prints the normal constructor as I expected and only the normal constructor. However, if I make the copy constructor a private member of the class, the compiler gives me the error
error: ‘Bla::Bla(const Bla&)’ is private within this context
From the looks of it, it looks like the copy constructor was called, but I do not see anything being printed from it. Is the copy constructor being implicitly called? What's going on here?
Before C++17, the copy operation might be elided but the copy constructor still needs to be present and accessible.
This is an optimization: even when it takes place and the copy/move (since C++11) constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed:
Since C++17 there's no such issue because of mandatory copy elision.
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:
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.
I have been learning about (N)RVO for a last few days. As I read on cppreference in the copy elision article, for C++14 :
... the compilers are permitted, but not required to omit the copy- and move- (since C++11)construction of class objects even if the copy/move (since C++11) constructor and the destructor have observable side-effects. This is an optimization: even when it takes place and the copy-/move-constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.
So, either copy or move constructor must be present and accessible. But in the code bellow:
#include <iostream>
class myClass
{
public:
myClass() { std::cout << "Constructor" << std::endl; }
~myClass() { std::cout << "Destructor" << std::endl; }
myClass(myClass const&) { std::cout << "COPY constructor" << std::endl;}
myClass(myClass &&) = delete;
};
myClass foo()
{
return myClass{};
}
int main()
{
myClass m = foo();
return 0;
}
I got the following error: test.cpp: In function 'myClass foo()':
test.cpp:15:17: error: use of deleted function 'myClass::myClass(myClass&&)'
return myClass{};. I get this error even if I don't call foo() from the main(). The same issue with NRVO.
Hence the move constructor is always required, isn't it? (while the copy is not, I checked it)
I do not understand where compiler needs a move constructor. My only guess is that it might be required for constructing a temporary variable, but it sounds doubtful. Do someone knows an answer?
About the compiler: I tried it on g++ and VS compilers, you can check it online: http://rextester.com/HFT30137.
P.S. I know that in C++17 standard the RVO is obligated. But the NRVO isn't, so I want to study out what is going on here to understand when I can use NRVO.
Cited from cppreference:
Deleted functions
If, instead of a function body, the special syntax = delete ; is used, the function is defined as deleted.
...
If the function is overloaded, overload resolution takes place first, and the program is only ill-formed if the deleted function was selected.
It's not the same thing if you explicitly define the move constructor to be deleted. Here because of the presence of this deleted move constructor, although the copy constructor can match, the move constructor is better, thus the move constructor is selected during overload resolution.
If you remove the explicit delete, then the copy constructor will be selected and your program will compile.
This question refers only to pre C++11. Consider the following seemingly broken code:
struct X
{
X(){} // default user-provided constructor
private:
X(const X&){}
};
int main()
{
X x = X();
}
Live on Coliru
According to cppreference.com in pre C++11 the default ctor will be called:
The effects of value initialization are:
1) if T is a class type with at least one user-provided constructor of any kind, the default constructor is called;
...
This seem to imply that the copy ctor doesn't necessarily need to be accessible. Is this correct or not? The code above does not compile, so it seems that a copy ctor must be accessible.
Value initialization doesn't require it, but you need an accessible copy constructor to do this:
X x = X();
That's copy initialization, which requires an accessible copy constructor. Even though it will not call that copy constructor, it still needs to exist.
C++17 might be the first version where that need will be lifted.
#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
I've already tried to ask this question but I wasn't clear enough. So here is one more try. And I am very sorry for my English ;)
Let's see the code:
#include <iostream>
#include <memory>
using namespace std;
struct A {
unique_ptr<int> ref;
void printRef() {
if (ref.get())
cout<<"i="<<*ref<<endl;
else
cout<<"i=NULL"<<endl;
}
A(const int i) : ref(new int(i)) {
cout<<"Constructor with ";
printRef();
}
~A() {
cout<<"Destructor with";
printRef();
}
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
It can not be compiled because unique_ptr has deleted copying constructor.
Orly?!
This class DOES HAVE an implied moving constructor because unique_ptr has one.
Let's do a test:
#include <iostream>
#include <memory>
using namespace std;
struct A {
unique_ptr<int> ref;
void printRef() {
if (ref.get())
cout<<"i="<<*ref<<endl;
else
cout<<"i=NULL"<<endl;
}
A(const int i) : ref(new int(i)) {
cout<<"Constructor with ";
printRef();
}
// Let's add a moving constructor.
A(A&& a) : ref(std::move(a.ref)) {
cout<<"Moving constructor with";
printRef();
}
~A() {
cout<<"Destructor with";
printRef();
}
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
I've added a moving constructor and now the code can be compiled and executed.
Even if the moving constructor is not used.
The output:
Constructor with i=0
Constructor with i=1
Destructor withi=1
Destructor withi=0
Okay...Let's do one more test and remove the copying constructor (but leave the moving one).
I don't post the code, there only one line has been added:
A(const A& a) = delete;
You should trust me - it works. So the compiler doesn't require a copying constructor.
But it did! (a facepalm should be here)
So what's going on? I see it completely illogical! Or is there some sort of twisted logic I don't see?
Once more:
unique_ptr has a moving constructor and has a deleted copying constructor. Compiler requires copying constructor to be present. But in fact the compiler requires a moving constructor (even if it is not used) and doesn't require a copying (because it could be deleted). And as I see the moving constructor is (should be?) present impliedly.
What's wrong with that?
P.S. One more thing - if I delete the moving constructor the program could not be compiled. So the moving constructor is required, but not the copying one.Why does it require copy-constructor if it's prohibited to use it there?
P.P.S.
Big thanks to juanchopanza's answer! This can be solved by:
A(A&& a) = default;
And also big thanks to Matt McNabb.
As I see it now, the moving constructor is absent because unique_ptr doesn't have a copying one the class has a destructor (and the general rule is that default/copying/moving constructors and destructor could be generated by default only all together). Then the compiler doesn't stop at moving one (why?!) and falls back to copying one. At this point the compiler can't generate it and stops with an error (about the copy constructor) when nothing else can be done.
By the way it you add:
A(A&& a) = delete;
A(const A& a) = default;
It could NOT be compiled with error about 'A::A(A&& a)' deletion, There will be no fall back to copying constructor.
P.P.P.S The last question - why does it stop with error at the COPY constructor but not the MOVE constructor?!
GCC++ 4.7/4.8 says: "error: use of deleted function ‘A::A(const A&)’"
So it stops at copy constructor.
Why?! There should be 'A::A(A&&)'
Ok. Now it seems like a question about move/copy constrcutor choosing rule.
I've created the new more specific question here
This is called copy elision.
The rule in this situation is that a copy/move operation is specified, but the compiler is allowed to optionally elide it as an optimization, even if the copy/move constructor had side-effects.
When copy elision happens, typically the object is created directly in the memory space of the destination; instead of creating a new object and then copy/moving it over to the destination and deleting the first object.
The copy/move constructor still has to actually be present, otherwise we would end up with stupid situations where the code appears to compile, but then fails to compile later when the compiler decides not to do copy-elision. Or the code would work on some compilers and break on other compilers, or if you used different compiler switches.
In your first example you do not declare a copy nor a move constructor. This means that it gets an implicitly-defined copy-constructor.
However, there is a rule that if a class has a user-defined destructor then it does not get an implicitly-defined move constructor. Don't ask me why this rule exists, but it does (see [class.copy]#9 for reference).
Now, the exact wording of the standard is important here. In [class.copy]#13 it says:
A copy/move constructor that is defaulted and not defined as deleted is implicitly defined if it is odr-used (3.2)
[Note: The copy/move constructor is implicitly defined even if the implementation elided its odr-use (3.2, 12.2). —end note
The definition of odr-used is quite complicated, but the gist of it is that if you never attempt to copy the object then it will not try to generate the implicitly-defined copy constructor (and likewise for moving and move).
As T.C. explains on your previous thread though, the act of doing A a[2] = {0, 1}; does specify a copy/move, i.e. the value a[0] must be initialized either by copy or by move, from a temporary A(0). This temporary is able to undergo copy elision, but as I explain earlier, the right constructors must still exist so that the code would work if the compiler decides not to use copy elision in this case.
Since your class does not have a move constructor here, it cannot be moved. But the attempt to bind the temporary to a constructor of A still succeeds because there is a copy-constructor defined (albeit implicitly-defined). At that point, odr-use happens and it attempts to generate the copy-constructor and fails due to the unique_ptr.
In your second example, you provide a move-constructor but no copy-constructor. There is still an implicitly-declared copy-constructor which is not generated until it is odr-used, as before.
But the rules of overload resolution say that if a copy and a move are both possible, then the move constructor is used. So it does not odr-use the copy-constructor in this case and everything is fine.
In the third example, again the move-constructor wins overload resolution so it does not matter what how the copy-constructor is defined.
I think you are asking why this
A a[2] = { 0, 1 };
fails to compile, while you would expect it to compile because A may have a move constructor. But it doesn't.
The reason is that A has a member that is not copyable, so its own copy constructor is deleted, and this counts as a user declared copy constructor has a user-declared destructor.
This in turn means A has no implicitly declared move constructor. You have to enable move construction, which you can do by defaulting the constructor:
A(A&&) = default;
To check whether a class is move constructible, you can use is_move_constructible, from the type_traits header:
std::cout << std::boolalpha;
std::cout << std::is_move_constructible<A>::value << std::endl;
This outputs false in your case.
The twisted logic is that you are supposed to write programs at a higher abstraction level. If an object has a copy constructor it can be copied, otherwise it cannot. If you tell the compiler this object shall not be copied it will obey you and not cheat. Once you tell it that it can be copied the compiler will try to make the copy as fast as possible, usually by avoiding the copy constructor.
As for the move constructor: It is an optimization. It tends to be faster to move an object from one place to another than to make an exact copy and destroy the old one. This is what move constructors are for. If there is no move constructor the move can still be done with the old fashioned copy and destroy method.