C++11 Standard specifies that auto generated move constructors should not be generated for classes declaring destructors, but the following
code snippet builds and runs correctly:
#include <iostream>
class C
{
public:
~C() { std::cout << "Called C:~C" << std::endl; }
private:
std::string v;
};
int main()
{
C c1;
C c2 = std::move(c1);
return 0;
}
I can build with clang 4.2.1 and gcc 4.4.3. Am i missing something?
The initialization uses the implicitly defined copy constructor. In general a move will always fall back to a copy when the move constructor or move assignment operator cannot be used for some reason, since the copy constructor and copy assignment operator are always declared (although they can be deleted under some circumstances).
Brian's answer already explains why your code compiles. To properly check that move ctor is supressed just put a non copyable object into your class, for example std::unique_ptr:
class C
{
public:
~C() { std::cout << "Called C:~C" << std::endl; }
private:
std::string v;
std::unique_ptr<char> p;
};
Now gcc 5.1.0 produces this error:
error: use of deleted function ‘C::C(const C&)’
C c2 = std::move(c1);
Related
I have this simple piece of code in C++17 and I was expecting that the move constructor was called (or the copy constructor, if I was doing something wrong), while it is just calling the normal constructor and I cannot why is doing this optimization.
I am compiling with -O0 option.
#include <iostream>
using namespace std;
struct Foo {
int m_x;
Foo(int x) : m_x(x) { cout << "Ctor" << endl; }
Foo(const Foo &other) : m_x(other.m_x) { cout << "Copy ctor" << endl; }
Foo(Foo &&other) : m_x(other.m_x) {
other.m_x = 0;
cout << "Move ctor" << endl;
}
};
void drop(Foo &&foo) {}
int main() { drop(Foo(5)); }
I cannot why is doing this optimization.
This is not due to any optimization in C++17. Instead this is due to the fact that you're passing an int when you wrote Foo(5). And since you've provided a converting constructor Foo::Foo(int), it will be used to create a Foo object which will then be bound to the rvalue reference parameter of drop.
Note that in C++17, even if we were to make the parameter of drop to be of type Foo instead of Foo&&, then also there will be no call to the move constructor because of mandatory copy elison.
C++11
On the other hand, if you were using C++11 and using the flag -fno-elide-constructors and parameter to drop was of type Foo instead of Foo&& then you could see that a call would be made to the move ctor.
//--------vvv-----------> parameter is of type Foo instead of Foo&&
void drop(Foo foo) {
std::cout<<"drop called"<<std::endl;
}
int main() {
drop(Foo(5)); //in c++11 with -fno-elide-constructors the move ctor will be called
}
The output of the above modified version in C++11 with -fno-elide-constructors is:
Ctor
Move ctor
drop called
Demo
In function main you create a temporary Foo object from integer 5 but you don't move (nor copy) from it anywhere. To actually call your move (or copy) constructor, you have to move- (or copy-) construct another object from your temporary Foo.
E.g., to call Foo's move constructor:
void drop(Foo &&foo) {
// Move-construct tmp from foo.
Foo tmp { std::move(foo) };
}
While testing some classes I run into an interesting problem: When invoking class constructor using the equals sign notation (=) if the copy constructor is deleted an error is encountered error: copying variable of type 'Class' invokes deleted constructor. While using the parenthesis the code compiles normally.
What is going on here? Can this be a compiler bug?
Consider the following class:
class Test
{
public:
int Int;
public:
Test() = default;
Test(Test &) = delete;
Test(Test &&) = delete;
Test(int i)
{
Int = i;
}
};
Constructors invoked like so:
Test t1(3); //No error
Test t2 = 3; //error: copying variable of type 'Class' invokes deleted constructor
Just to check I tried to add some checks and allow these functions and compile the code.
Both constructors compiled using MSVC in the exact same way.
class Test
{
public:
int Int;
public:
Test()
{
Int = 0;
cout << "Constructor";
}
Test(Test &t)
{
Int = t.Int;
cout << "Copy Constructor";
}
Test(Test &&t)
{
Int = t.Int;
cout << "Move Constructor";
}
Test(int i)
{
Int = i;
cout << "Constructor from int";
}
};
Test t1(3); //Constructor from int
Test t2 = 3; //Constructor from int
What exactly is going on here?
You're seeing the results of the copy elision rule.
Basically, saying T var = expr; constructs an unnamed temp from expr and then copies or moves it into var using the copy or move construtor. If the copy and move constructors are deleted, then this gives an error about the deleted constructor. But then, the compiler is required to elide that copy or move and construct var directly from expr, even if the copy or move constructor has visible side effects. Its one of those weird corner cases that arises from language design by de-facto standardization of what disparate implementations do (or did at some time in the past), plus design-by-committee and slow evolution over time while trying to keep backwards compatibility.
see here for more discussion
This question already has answers here:
Is there a difference between copy initialization and direct initialization?
(9 answers)
Closed 6 years ago.
The following is excerpted from section 13.1.1. from book "C++ Prime", 5th edition:
To verify the above paragraph, especially the statement underlined with red, I wrote the following codes:
#include<iostream>
using namespace std;
class TestClass {
public:
TestClass() :a(7) {
cout << "Default constructor";
}
TestClass(int aa) : a(aa) {
cout << "Constructor" << endl;
}
TestClass(const TestClass & t): a(t.a) {
cout << "Copy constructor" << endl;
}
TestClass & operator=(const TestClass & rhs) {
a = rhs.a;
return *this;
}
int a;
};
int main() {
TestClass t1(1);
TestClass t2 = t1;
}
Based on my understanding of the description of copy initialization in the book, the code should first create t2 using default initializer, then use operator= function to copy the right-hand operand t1. But when I debug line by line in Visual Studio 2015, the code go straight to the copy constructor TestClass(const TestClass & t). This shows that direct initialization and copy initialization are actually doing the same thing, no difference. So, is my understanding wrong or the book is wrong? If I am wrong, what is the correct understanding of the difference between direct initialization and copy initialization? Could you please give me an example code to show such difference? Thanks a lot.
Edit: someone says my question can be answered in this thread. But that thread is only a (detailed and lengthened) repeat of the text I excerpted. It doesn't answer why in practice (e.g., Visual Studio 2015) it is not true.
The book just says "copy", which doesn't just mean copy assignment. Note the word "created", copy initialization means construction, not assignment.
For TestClass t2 = t1;, t2 will be copy constructed from t1 via copy constructor directly, not default construction and then assignment.
If T is a class type and the cv-unqualified version of the type of other is T or a class derived from T, the non-explicit constructors of T are examined and the best match is selected by overload resolution. The constructor is then called to initialize the object.
Yes, copy initialization and direct initialization have the same effect in most cases, but there's a difference between them.
Copy-initialization is less permissive than direct-initialization: explicit constructors are not converting constructors and are not considered for copy-initialization.
e.g.
class TestClass {
public:
// the copy constructor is declared explicit now
explicit TestClass(const TestClass & t): a(t.a) {
cout << "Copy constructor" << endl;
}
TestClass(int aa) : a(aa) {
cout << "Constructor" << endl;
}
int a;
};
then
int main() {
TestClass t0(1);
TestClass t1(t0); // fine; explicit constructor works fine with direct initialization
TestClass t2 = t0; // error; explicit constructor won't be considered for copy initialization
}
Let's start with this small example:
#include <vector>
#include <iostream>
using namespace std;
class A {
private:
A& operator =(const A&);
};
int main(void) {
vector<A> v;
v = { A() };
return 0;
}
Compilation of this code fails with the error message error: ‘A& A::operator=(const A&)’ is private. I have no idea why it needs the assignment operator so I tried to find out and changed the code to this:
#include <vector>
#include <iostream>
using namespace std;
class A {
public:
A& operator =(const A& a) { cout << "operator=" << endl; return *this; }
};
int main(void) {
vector<A> v;
v = { A() };
return 0;
}
Now the code compiles but when I execute it it does not output the debug message in the assignment operator implementation.
So the compiler wants the assignment operator but doesn't use it? I guess the compiler optimizes the assignment away somehow. Just like it optimizes the usage of move constructors (Which can be prevented with the option -no-elide-constructors). Is there a compiler option which can prevent assignment optimimzations? Or is there a different explanation why the compiler wants to have an accessible assignment operator but doesn't use it during runtime?
In C++03, types being stored in a container need to be CopyConstructible and Assignable. In C++11, requirements are relaxed and applied to the operations performed on the container.
class A needs to be CopyConstructible and Assignable because being stored in vector That's why you need public operator=
int main(void) {
vector<A> v;
v = { A() }; // Copy Constructor
A x;
x = v[0]; // operator=
return 0;
}
I little bit late but I still want to answer your question.
Your example shows a standard copy elision of C++. This is also discussed in another question.
That is, the compiler checks the correctness of your operation. You have to call a copy constructor right after default constructor to use vector and put your class inside, but call the default constructor only in order to improve performance.
C++ 11 solves the issue with the move constructor.
Here is the situation I came up with:
#include <iostream>
using namespace std;
struct test {
test() { cout << "ctor" << endl; }
test(const test&) = delete;
test(test&&) = delete;
};
auto f() -> test {
return {};
// return test{};
}
auto main() -> int {
f();
}
This code compiles with both clang and gcc, but when I change return {} to return test{} it doesn't compile anymore. Why is that? Shouldn't it work the same in both cases?
Frankly, I don't know if there is a good use case for this, but it caught me by surprise, so now I'm wondering what's going on.
return {} uses an empty initialiser list to initialise the return value, using the default constructor.
return test{} creates a temporary using the default constructor, then uses that to initialise the return value using a move or copy constructor. You have deleted those constructors, so that can't be done.
In practice, the copy or move will be elided so that both have the same effect - but the second still requires an accessible constructor, even if it's not actually used.
As an extension of Mike's answer:
int main()
{
// Error: Call to deleted move constructor (in C++98 the compiler would
// shout at the private copy constructor).
// auto t = test{};
// OK: Default constructor used
test t2;
test t3{};
return 0;
}
Even though the move/copy is elided, the C++ standard requires visibility of these constructors.