Autogenerated move constructors causing illegal behavior - c++

I asked a question about move constructors for which I haven't accepted an answer yet because I'm feeling more confused about certain aspects of the question even as I'm starting to get a grip on others. In particular, I've found a surprising case in which both g++ and clang++ generate incorrect move-constructors.
Question summary
g++ and clang++ apparently violate the rule that move-constructors are not generated when destructors are explicitly defined; why? Is this a bug, or am I misunderstanding what's going on?
For correctness, these (possibly-illegal) move constructors should invalidate RHS pointer members, but they don't. Why not?
It appears that the only way to avoid the unwanted behavior is to explicitly define a correct move constructor for every class that uses delete in its destructor. Does the Qt library (version 5.4) do this?
Part 1: Illegally auto-generated constructors?
Consider the following code:
class NoMove
{
public:
~NoMove() {}
};
int main()
{
std::cout << "NoMove move-constructible? " <<
std::is_move_constructible<NoMove>::value << std::endl;
}
Compiled with both g++ 4.9.2 and clang++ 3.5.1, this code prints:
NoMove move-constructible? 1
...But since NoMove has an explicitly defined destructor, I would expect that neither a move constructor nor a copy constructor should be auto-generated. Note that the unexpected constructor generation is not due to the fact that the destructor is trivial; I get the same behavior when the destructor delete[]s an array (!!), and I am even able to compile code that requires a valid move constructor (!!!!!). (See example below.) What's going on here? Is it legal to auto-generate a move constructor here, and if so, why?
Part 2: (Possibly illegal) auto-generated constructors causing undefined behavior?
It appears that providing safe move constructors when delete is involved is fairly simple, but I just want to make sure I understand: when a class contains a pointer member and owns the underlying data, is there any case in which it wouldn't be correct and sufficient for the move constructor to invalidate the RHS pointer after setting the destination pointer to the old value?
Consider the following example, which is similar to the NoMove example above and is based on my original question:
class DataType
{
public:
DataType()
{
val = new int[35];
}
~DataType()
{
delete[] val;
}
private:
int* val;
};
class Marshaller
{
public:
Marshaller()=default;
DataType toDataType() &&
{
return std::move(data);
}
private:
DataType data;
};
void DoMarshalling()
{
Marshaller marshaller;
// ... do some marshalling...
DataType marshalled_data{std::move(marshaller).toDataType()};
}
This compiles just fine--showing that, yes, DataType has an auto-generated move constructor. And of course, when run, it causes a double-deletion error.
Now, this would be okay, if the auto-generated move constructor invalidated the RHS pointer. So, if it's okay to auto-generate a move constructor here, why isn't that done safely? The move constructor that makes this work is simply:
DataType(DataType&& rhs) :
val{rhs.val}
{
rhs.val = nullptr;
}
(Right? Am I missing anything? Should it perhaps be val{std::move(rhs.val)}?)
This seems like it would be a perfectly safe function to auto-generate; the compiler knows that rhs is an r-value because the function prototype says so, and therefore it's entirely acceptable to modify it. So even if DataType's destructor didn't delete[] val, it seems like there wouldn't be any reason not to invalidate rhs in the auto-generated version, except, I suppose, for the fact that this leads to a trivial performance hit.
So if the compiler is auto-generating this method--which, again, it shouldn't, especially since we can just as easily get this exact behavior from standard library code using unique_ptr-- why is it auto-generating it incorrectly?
Part 3: Avoiding this behavior in Qt (especially QByteArray in Qt 5.4)
Finally, a (hopefully) easy question: do Qt 5.4's heap-allocating classes such as QByteArray (which is what I'm actually using as the DataType in my original question) have correctly implemented move constructors, invalidating any moved-from owning pointer(s)?
I wouldn't even bother to ask, because Qt seems pretty solid and I haven't seen any double-deletion errors yet, but given that I was taken off guard by these incorrect compiler-generated move constructors, I'm concerned that it's quite easy to end up with incorrect move constructors in an otherwise-well-implemented library.
Relatedly, what about Qt libraries written before C++11 that don't have explicit move-constructors? If I can accidentally coerce an auto-generated move constructor that behaves erroneously in this case, does anyone know if compiling, say, Qt 3 with a C++11-compliant compiler causes undefined destruction behavior in use-cases like this?

The problem is that you are confusing is_move_constructible and "has a move constructor". is_move_constructible<T> doesn't test whether T has a move constructor. It tests whether T can be constructed from an rvalue of type T. And const T& can bind to a T rvalue.
What you are seeing is the autogenerated copy constructor T(const T&) doing its work - and failing miserably.
I would expect that neither a move constructor nor a copy constructor should be auto-generated.
Your link talks about the move constructor. It doesn't talk about the copy constructor, which is always implicitly declared if you don't declare it.
Now, if you declared a move operation, the implicitly declared copy constructor would be defined as deleted, but you didn't do that, so it's defined as defaulted and performs a memberwise copy. [class.copy]/p7:
If the class definition does not explicitly declare a copy
constructor, one is declared implicitly. If the class definition
declares a move constructor or move assignment operator, the
implicitly declared copy constructor is defined as deleted; otherwise,
it is defined as defaulted (8.4). The latter case is deprecated if the
class has a user-declared copy assignment operator or a user-declared
destructor.

Related

What was preventing move constructor to be used for overload resolution if this is the only possible one?

Consider this snippet : OnlyMoveable can be built only with default constructor or move operation.
In this special case, at least for me, it seems obvious that there is no ambiguity and that constructing k shall call the move constructor.
Why automatic rvalue reference cast is not something in the standard ? That would have save many "verbose" std::move when manipulating move only types.
What would be in risk or unclear if such cast was done when no ambiguity was possible ?
struct OnlyMoveable {
OnlyMoveable() {}
OnlyMoveable(const OnlyMoveable& other) = delete;
OnlyMoveable(OnlyMoveable&& other) {}
};
void dummy ()
{
OnlyMoveable defaultCtor;
OnlyMoveable j(std::move(defaultCtor));
OnlyMoveable defaultCtor2;
// Do not compile, because attemps to call the copy ctor. Ok, but well, why the compiler in this special case where there is no
// other overload than the move, can automitcally perform the rvalue reference cast ?
//OnlyMoveable k(defaultCtor2);
};
Without the existence of a copy-constructor, this initialization won't compile:
OnlyMoveable k(defaultCtor2); // error
It might seem reasonable that this would call the move constructor. However, this has the major problem, that defaultCtor2 would be in a valid but unspecified state. This is dangerous and the language doesn't do this for you automatically, which is a good thing.
Of course, as the programmer, if you know that you never need to use defaultCtor2 again, or at least you won't read its value, you can use an explicit std::move to tell the compiler to use the move constructor:
OnlyMoveable k(std::move(defaultCtor2)); // ok

Why does the compiler require a copying constructor, need and have moving one and doesn't uses any of them?

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.

Default move constructor vs. Default copy constructor vs. Default assignment operator

Why does C++ compiler have more restriction on automatically generated move constructors than on automatically generated copy constructor or assignment operator ?
Automatically generated move constructors are generated only if user has defined nothing (i.e: constructor, copy, assignment, destructor..)
Copy constructor or assignment operator are generated only if user has not defined respectively copy constructor or assignment operator.
I wonder why the difference.
I believe backwards compatibility plays a big part here. If the user defines any of the "Rule of three" functions (copy ctor, copy assignment op, dtor), it can be assumed the class does some internal resource management. Implicitly defining a move constructor could suddenly make the class invalid when compiled under C++11.
Consider this example:
class Res
{
int *data;
public:
Res() : data(new int) {}
Res(const Res &arg) : data(new int(*arg.data)) {}
~Res() { delete data; }
};
Now if a default move constructor was generated for this class, its invocation would lead to a double deletion of data.
As for the move assignment operator preventing default move constructor definitions: if the move assignment operator does something other than default one, it would most likely be wrong to use the default move constructor. That's just the "Rule of three"/"Rule of five" in effect.
As far as I know, this is because of downward compatibility. Consider classes written in C++ (before C++11) and what would happen if C++11 would start to automatically generate move-ctors in parallel to existing copy-ctors or generally any other ctor. It would easily break existing code, by-passing the copy-ctor the author of that class wrote. Hence, the rules for generating a move-ctor where crafted to only apply to "safe" cases.
Here's the article from Dave Abrahams about why implicit move must go, which eventually led to the current rules of C++11.
And this is an example how it would fail:
// NOTE: This example assumes an implicitly generated move-ctor
class X
{
private:
std::vector<int> v;
public:
// invariant: v.size() == 5
X() : v(5) {}
~X()
{
std::cout << v[0] << std::endl;
}
};
int main()
{
std::vector<X> y;
// and here is where it would fail:
// X() is an rvalue: copied in C++03, moved in C++0x
// the classes' invariant breaks and the dtor will illegally access v[0].
y.push_back(X());
}
When C++ was created, it was decided that default constructor, copy-constructor, assignment-operator and destructor would be generated automatically (unless provided). Why ? Because C++ compilers should be able to compile (most) C code with identical semantics, and that's how struct work in C.
However, it was later noticed that whenever a user writes a custom destructor, she probably needs to write a custom copy-constructor/assignment-operator too; this is known as the Rule of Big Three. With hindsight, we can see that it could have been specified that the generated copy-constructor/assignment-operator/destructor would have only been generated if none of the 3 were user-provided, and it would have helped catch a lot of bugs... and still retain backward compatibility with C.
Therefore, as C++11 came around, it was decided that this time things would be done right: the new move-constructor and move-assignment-operator would only be generated automatically if it was clear that the user was not doing anything "special" with the class. Anything "special" being defined as redefining move/copy/destruction behavior.
To help with the case were people would be doing something special but still wanted "automatically generated" special methods, the = default sugar-coating was added as well.
Unfortunately, for backward compatibility reasons, the C++ committee could not go back in time and change the rules of automatic generation for copy; I wish they had deprecated it to pave the way for the next version of the Standard, but I doubt they will. it is however deprecated (see §12.8/7 for the copy constructor for example, courtesy of #Nevin).

std::unique_ptr compiler error: Members of a derived class cannot access private members of a base class

I get Compiler Error C2248 when i try to compile the following code:
#include <list>
#include <memory>
using namespace std;
class data
{
public:
static data parse()
{
data d;
data::parse(d);
return d;
}
list<std::unique_ptr<data>> l;
private:
static void parse(data& node)
{ }
};
int main()
{
return 0;
}
Why? How can i fix this?
Note: I have no problem using std::shared_ptr instead of std::unique_ptr.
You need to provide move operations for your type:
data(data&& other)
: l(std::move(other.l))
{
}
data& operator=(data&& other)
{
l = std::move(other.l);
return *this;
}
And, since you'll have added a user-declared constructor, you'll also need a user-declared default constructor:
data() { }
My understanding is that your code is correct as-is, per the final C++11 language standard. Visual C++ does not fully implement the final specification for when move operations are implicitly generated (as of the Visual C++ 2012 RC). The specification for when implicit move operations are generated changed several times very late in the standardization process.
If you have a class type C that has any data member that is movable but noncopyable, Visual C++ will not generate an implicit move constructor or move assignment operator, and the implicit copy constructor and copy assignment operator are both suppressed by the presence of the move-only data member. In other words, if you want ot aggregate move-only types, you must provide the move operations for the aggregating class yourself.
(At least, this is my understanding from experimentation with the compiler.)
First things first, VC++ doesn't automatically generate a move ctor and move assignment operator yet, which means you need to define them yourself.
Next, when you return local variables, the compiler first tries to move them before actually going the usual route of copying them. However, to do that, it needs a move ctor. Since it doesn't have that, it tries the usual copy and through the generated copy ctor automatically invokes the copy constructor of std::list which in turn tries to invoke the copy ctor of its element type, which is private in std::unique_ptrs case.
You need to either define an appropriate move ctor or a copy ctor that doesn't invoke std::unique_ptr's copy ctor (i.e., make a deep copy of the content).
Short answer: (C++11 specific) Items in a list must be copyable or movveable. A unique_ptr is not copyable by-design, but it is moveable, so long as the controlled type is also moveable.
Your type, data is not moveable because you have not implemented move semantics and the compiler did not do it for you.
Implement move semantics, and you can use unique_ptr in a list:
data(ddata&&) {};
According to thhe Standard, a move constructor would be generated for your class by the compiler. However, VS10 does not support this -- this might be the problem your'e running in to.
For further reference, see my post on CR: Canonical Implementation of Move Semantics

Why is the compiler bugging me on this one?

(using Visual C++ 2010, compiling in debug with optimizations turned off)
I have the following very simple class:
class exampleClass
{
public:
exampleClass()
{
cout << "in the default ctor" << endl;
}
private:
exampleClass (const exampleClass& e)
{
cout << "in the copy ctor" << endl;
}
};
When I try to compile it with the following main:
#include <iostream>
using namespace std;
int main()
{
exampleClass e1=exampleClass();
return 0;
}
I get the compilation error:
'exampleClass::exampleClass' : cannot access private
member declared in class 'exampleClass'
When I remove the access modifier "private" from the copy ctor, the program compiles and prints only:
in the default ctor
Why is this happening? If the compiler will not invoke the copy ctor anyway, why is it bugging me?
Since some people missed the first line (at least before some edits) i will repeat it:
I compiled in debug with optimizations turned off.
This type of initialization is called copy-initialization. I believe the following clause from the C++11 standard applies here (paragraph 8.5.16, page 204):
If the initialization is direct-initialization, or if it is
copy-initialization where the cv-unqualified version of the source
type is the same class as, or a derived class of, the class of the
destination, constructors are considered. The applicable constructors
are enumerated (13.3.1.3), and the best one is chosen through overload
resolution (13.3). The constructor so selected is called to initialize
the object, with the initializer expression or expression-list as its
argument(s). If no constructor applies, or the overload resolution is
ambiguous, the initialization is ill-formed.
In this case, the best applicable constructor is the copy ctor, which is private, hence the error message.
To further answer your question, when the copy ctor is private, your program is simply not allowed to pass the complier check because of the rules imposed by the standard. When you make the copy ctor public, the program becomes valid, but the call to the copy ctor is optimized away.
EDIT:
Okay, to elaborate on previous paragraph.You're dealing here with the so-called copy elision. While the copy elision is possible in this case, the standard requires you to provide an accessible copy ctor for your class.
exampleClass e1=exampleClass();
This will first create a temporary exampleClass using the default constructor and then copy that into e1 using the copy constructor. This will invoke the private copy constructor and thus give you the error. The corrent way to instantiate an instance of a class with the default constructor is this:
exampleClass e1;
The compiler is required to bug you there. While the copy can be elided, the standard requires that the copy constructor is accessible for that type of construction. Of course, you can simplify the code and avoid the copy construction altogether:
exampleClass e1; // Will call exampleClass::exampleClass()
exampleClass e1=exampleClass();
is the same as:
exampleClass e1(exampleClass());
i.e it invokes the (private) copy constructor.
This is because at compile time, the compiler checks if the function the user is trying to access is really accessible. So when you use exampleClass e1=exampleClass();, it first checks if the copy-constructor is accessible. It spits out an error because the copy-constructor is not private. Remember that at this point the compiler hasn't gone onto the optimization stage where it does the clever stuff as to skip the copy-constructor.
When you make the copy-constructor public, the compiler successfully goes through the stage of parsing the code and making sure that everything is accessible and is in order (there's actually more than that going on) and then at the optimization stage, which usually is on in 'Release' mode it does the clever stuff and by-passes the use of copy-constructor. However if you tried the same code in 'Debug' mode you'd see that the copy-constructor does get called.
Everyone explains how you should instantiate an object and #Grigory Javadyan makes a good point on copy elision. It looks like, MSVC does this optimization (so called return value optimization) even in debug mode.
exampleClass e1=exampleClass();
is the same as
exampleClass giveExample()
{
return exampleClass();
}
exampleClass e1 = giveExample();
You will see that copy ctor will not be called.
But here :
exampleClass giveExample()
{
exampleClass example;
return example;
}
exampleClass e1 = giveExample();
you will see another output line :
in the copy ctor
Because you are forcing the compiler to first generate an object and then return it.
Here, here and here some questions I can find, similar to yours.
PS. Link#2 is from another Q&A site. I hope this is not a problem.
That's not how you do instantiate an object in C++. If you want it allocated on the stack, you write:
exampleClass e1;
and you're done, sincee exampleClass' constructor accepts no parameters.
Otherwise, if you want it allocated on the heap, you write:
exampleClass e1 = new exampleClass();
The way you wrote it actually creates a temporary object and invokates the copy constructor on that temporary object to create e1. Problem is that your copy-ctor is private, so the compiler's error message.
when you write
exampleClass e1 = exampleClass()
it is the same as writing
exampleClass e1( exampleClass() );
which invokes the copy ctor.
Its because the copy constructor is private..
your code is
creating a temporary exampleClass and invoking the default constructor exampleClass()
attempting to assign the resulting temporary object to e1 using the private copy constructor