The following code works OK:
#include <iostream>
using namespace std;
struct oops
{
~oops()
{
cout << " oops! " << endl;
}
};
struct sample
{
oops* x = nullptr;
sample(oops* p) : x(p)
{
cout << "sample: " << p << endl;
}
~sample()
{
delete x;
cout << "destroy sample " << endl;
}
sample(const sample&)
{
cout << "copy sample " << endl;
}
sample(sample&&)
{
cout << "move sample " << endl;
}
};
int main()
{
sample s = new oops;
return 0;
}
Result:
sample: 0x1470c20
oops!
destroy sample
It clearly shows that neither the move nor copy constructor was called.
When these constructors are deleted,
sample(const sample&) = delete;
sample(sample&&) = delete;
gcc gives a compile error:
bpp.cpp: In function ‘int main()’:
bpp.cpp:29:17: error: use of deleted function ‘sample::sample(sample&&)’
sample s = new oops;
^
bpp.cpp:24:2: note: declared here
sample(sample&&) = delete;
^
bpp.cpp:14:2: note: after user-defined conversion: sample::sample(oops*)
sample(oops* p) : x(p)
^
Does this have anything to do with -fno-elide-constructors?
How can I compile it without defining these constructors or using an explicit constructor?
Edit:
My GCC verison is 5.4.0. The command is:
g++ bpp.cpp -std=c++17
sample s = new oops;
This is a form of copy initialization. For a compiler to resolve it until C++17, a copy or move constructor must exist. However, a compiler is free to elide its call due to optimizations (with GCC and -fno-elide-constructors, the move constructor is called).
Since C++17, neither of these constructors is required: https://wandbox.org/permlink/3V8glnpqF5QxljJl.
How can I compile it without defining these constructors or using explicit constructor?
Very simply, avoid copy initialization and use direct initialization instead:
sample s { new oops };
Or, use C++17.
Why does this fail to compile?
This line of code:
sample s = new oops;
is equivalent to writing:
sample s = sample(new oops);
In C++11 and C++14, this implicitly calls the move constructor (or the copy constructor, if no move constructor is available). Because compilers are allowed to elide copies and moves, the actual move is elided, and it doesn't show anything when the move constructor is called. Even though the actual move is elided, programs aren't allowed to refer to deleted functions even implicitly, so there's a compiler error.
You can fix this by changing the initialization to either
sample s { new oops };
or
sample s ( new oops );
or, if you really wanna use =, you can take advantage of temporary lifetime extension to write
// s won't get destroyed until the end of the scope
// it's safe to use s after this statement
sample&& s = new oops;
What does C++17 change?
C++17 made some changes to the set of value categories objects fall in. In C++17, sample(new oops) becomes a prvalue, and the c++17 standard mandates that compilers are required to yeet prvalues into place without copying it or moving them. This is done through a combination of dark magic and sorcery.
This means that
sample s = new oops;
is legal in c++17.
Why does it still fail to compile when you add -std=c++17?
This code should compile under C++17, and the error you're getting occurs because gcc 6.3 and earlier don't implement that part of the c++17 standard. This issue was fixed in gcc 7.1 and the code will compile as expected.
Related
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.
I met one interesting compiling error when testing one small example:
#include <iostream>
using namespace std;
class A
{
public:
A() { cout <<"A created." <<endl; }
A(A& a) { cout <<"A created by copy constructor." <<endl; }
~A() { cout <<"A destoryed." <<endl; }
};
A CreateObject()
{
A obj;
return obj;
}
int main()
{
A a;
A b;
b= CreateObject();
return 0;
}
it's simple and may not have any issues at all. however, it prompted in compiling:
copy_constructor.cpp: In function ‘int main()’:
copy_constructor.cpp:23: error: no matching function for call to ‘A::A(A)’
copy_constructor.cpp:9: note: candidates are: A::A(A&)
it seems that the program tried to call copy constructor when compiling "b= CreateObject();" but there is no matching copy constructor. this should not happen because it just one assignment statement, is there any difference between constructor and normal function in compiling optimization ?
A(A& a) { cout <<"A created by copy constructor." <<endl; }
...
b = CreateObject();
This is invalid according to C++03 standard.
In C++03, b = CreateObject() may actually expand to b = A(CreateObject());. This has only been "fixed" in later versions of C++, where the creation of the temporary copy is now prohibited.
CreateObject() returns a r-hand value which can only be consumed by copy constructor with a A(const A& a) signature. Without the const modifier, it's only applicable to l-hand values.
E.g. b = a would still have been valid with that signature and the expansion, because you would be allowed to modify a inside the constructor.
This is still replicable setting clang++ to C++98 standard: http://coliru.stacked-crooked.com/a/50c25c469420ab0f
Older versions of Visual-C++ exhibit the precise error shown by the OP.
g++ fails to validate this correctly, even when explictly specifying C++98.
See also https://stackoverflow.com/a/13898867/2879325
The code you've posted compiles just fine. You should however change your copy constructor from A(A&) to A(const A&).
After trying to write an example regarding move constructors, I ran into the following code:
#include <utility>
#include <iostream>
using namespace std;
class Data
{
public:
Data()
: x (3)
{
cout << "Data()" << endl;
}
Data(Data&&)
: x(4)
{
cout << "Data(&&)" << endl;
}
int x;
};
int main()
{
Data a;
Data b (std::move(a));
cout << b.x << endl;
return 0;
}
Why is the move constructor not called here?
The program prints:
Data()
3
What I'm finding even weirder is that by adding a copy constructor, suddenly, it does call the move constructor...
Data(const Data&)
: x(2)
{
cout << "Data(copy)" << endl;
}
And now it will print
Data(&&)
4
P.S I'm using gcc 4.4.5
Well, your code works properly for me. See this sample.
Output:
Data()
Data(&&)
4
As standard says:
The move constructor is called whenever an object is initialized from
xvalue of the same type, which includes
initialization, T a = std::move(b); or T a(std::move(b));, where b is of type T
function argument passing: f(std::move(a));, where a is of type T and f is void f(T t)
function return: return a; inside a function such as T f(), where a is of type T which has a move constructor.
And
std::move obtains an rvalue reference to its argument and converts it to an xvalue.
I see no reason for behavior you describe. Perhaps there is something wrong with your compiler?
EDIT
It seems, that it is indeed the fault of the compiler. Definition of move functions was described in proposal N3053 ("Defining Move Special Member Functions"). As we can see in table on this page:
Your code is well-formed and should call the move constructor. However, gcc 4.4, does not support defining move functions as stated here.
You do want to consider to update your compiler.
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.
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.