Environment: VS2015 Update 3, 64 bit (debug|release) compile
In the code below, if I uncomment the Maybe(X&) = delete line, I get the warning mentioned in the code below and in the title of the question.
Now, I am aware, that there are certain rules in C++(11?), which might render
the explicit deletion of that constructor obsolete. Only, even after searching
the web for a while, I could not find a definite rule, which would confirm, that
if I only delete Maybe(const X&) = delete, the compiler will not auto generate the other copy constructor.
So my question is first and foremost:
Can anyone point me to spot in the C++ specification, which clearly defines the
rules for auto generation of copy constructors? Alternatively, some less official
easy to remember rule of thumb on how to be certain of what will happen would also be welcome.
template <class X>
class Maybe
{
X *m_just;
public:
explicit Maybe(const X& x)
: m_just(new X(x))
{}
Maybe()
: m_just(nullptr)
{}
Maybe(const Maybe<X>&& other)
: m_just(other.m_just)
{
other.m_just = nullptr;
}
Maybe(const Maybe<X>&) = delete;
// If line below is uncommented, this produces the warning:
// warning C4521: 'Maybe<Int32>': multiple copy constructors specified
// Maybe(Maybe<X>&) = delete;
~Maybe()
{
delete m_just;
m_just = nullptr;
}
// ... more members and code which are not related to question
// ...
};
Please do not comment on the whole idea of that class. It is just private tinkering in my private lab... ;)
The compiler will only generate one of Maybe(Maybe<X>&) and Maybe(const Maybe<X>&).
The conditions for the choice are listed in section 12.8, paragraph 8:
The implicitly-declared copy constructor for a class X will have the
form X::X(const X&) if
each direct or virtual base class B of X has a copy constructor whose first parameter is of type const B& or const volatile B&, and
for all the non-static data members of X that are of a class type M (or array thereof), each such class type has a copy constructor whose
first parameter is of type const M& or const volatile M&
Otherwise, the implicitly-declared copy constructor will have the form
X::X(X&)
Or more informally, the parameter will be const if and only if everything that needs to be copied can be "const-copied".
Deleting one will not cause the other to be generated.
Since you have no base class and your only member is a pointer, the generated constructor will be of the const variety and you can leave out the line causing an error.
My C++ is a little bit rusty so this might be wrong, but I don't think you should even have a non-const copy constructor (except for the move constructor naturally).
If a = b changes b you're going to surprise some people. Perhaps it's legitimate for the copy constructor to change the source in a way that's not externally visible, but in that case I think you're better off using const_cast inside the const copy constructor.
The error I don't think is about delete, it's about defining multiple copy constructors, which isn't allowed. I suspect even allowing the non-const copy constructor is a bit of a wart in the language.
other.m_just = nullptr;
This line should not compile. Something must be wrong with your compiler, or your code example doesn't exactly reflect what you're compiling.
Anyway, the move constructor should take a non-const rvalue reference.
The commented line is simply not necessary. Just leave it out. If you define the copy constructor (even as deleted), no other form of the copy constructor is defined by the compiler, ever, so if you leave the line out, there simply is no constructor that takes a non-const reference to Self, meaning that overload resolution will just pick the const reference copy constructor, which is deleted anyway.
Related
While I was learning std::move, I found a strange issue.
If I add only a destructor that do nothing to a perfect program, I will get a compile error.
#include <iostream>
using namespace std;
class M {
public:
int database = 0;
M &operator=(M &&other) {
this->database = other.database;
other.database = 0;
return *this;
}
M(M &&other) { *this = std::move(other); }
M(M &m) = default;
M() = default;
~M() { /* free db */ }
};
class B {
public:
M shouldMove;
//~B(){} //<--- ## Adding this line will cause compile error. ##
};
int main() {
B b;
B b2 = std::move(b); //## error at this line if the above line is added
return 0;
}
Live code: https://ideone.com/UTR9ob
The error is invalid initialization of non-const reference of type 'B&' from an rvalue of type 'std::remove_reference<B&>::type {aka B}'.
Question:
(1) Which rules of C++ syntax enforce that? In other words, what does the error mean?
(2) If I want to add destructor that do almost nothing (e.g. only print debug log) to B, do I really have to follow the rule-of-five instead? If no, how to make it compile? Following the rule of five just because of that is too tedious and dirty in my opinion.
I think the rule of zero is just a good practice.
However, from this example, it seems to me that it is a hard rule that if violated, I will get compile error.
The implicitly-declared move constructor is only present if a class does not have a user-declared destructor. Therefore the answer to 2. is YES.
The answer to 1. is that this is hard rule and can be found in 12.8, paragraph 9 of the standard:
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared
as defaulted if and only if
X does not have a user-declared copy constructor,
X does not have a user-declared copy assignment operator,
X does not have a user-declared move assignment operator,
X does not have a user-declared destructor, and
the move constructor would not be implicitly defined as deleted.
[ Note: When the move constructor is not implicitly declared or explicitly supplied, expressions that
otherwise would have invoked the move constructor may instead invoke a copy constructor. — end note ]
The best way of getting this to run,is by using something like a smart pointer, i.e., a base class or member that does define all five special members (and very little else) so that you don't have to. In this case, an integer handle equivalent to std::unique_pointer should work well. However, keep in mind that databases, like files, can have errors while closing, so standard non-throwing destructor semantics don't cover all cases.
In C++11, one can explicitly default a special member function, if its implicit generation was automatically prevented.
However, explicitly defaulting a special member function only undoes the implicit deletion caused by manually declaring some of the other special member functions (copy operations, destructor, etc.), it does not force the compiler to generate the function and the code is considered to be well formed even if the function can't in fact be generated.
Consider the following scenario:
struct A
{
A () = default;
A (const A&) = default;
A (A&&) = delete; // Move constructor is deleted here
};
struct B
{
B () = default;
B (const B&) = default;
B (B&&) = default; // Move constructor is defaulted here
A a;
};
The move constructor in B will not be generated by the compiler, because doing so would cause a compilation error (A's move constructor is deleted). Without explicitly deleting A's constructor, B's move constructor would be generated as expected (copying A, rather than moving it).
Attempting to move such an object will silently use the copy constructor instead:
B b;
B b2 (std::move(b)); // Will call B's copy constructor
Is there a way to force the compiler into either generating the function or issue a compilation error if it can't? Without this guarantee, it's difficult to rely on defaulted move constructors, if a single deleted constructor can disable move for entire object hierarchies.
There is a way to detect types like A. But only if the type explicitly deletes the move constructor. If the move constructor is implicitly generated as deleted, then it will not participate in overload resolution. This is why B is movable even though A is not. B defaults the move constructor, which means it gets implicitly deleted, so copying happens.
B is therefore move constructible. However, A is not. So it's a simple matter of this:
struct B
{
static_assert(is_move_constructible<A>::value, "Oops...");
B () = default;
B (const B&) = default;
B (B&&) = default; // Move constructor is defaulted here
A a;
};
Now, there is no general way to cause any type which contains copy-only types to do what you want. That is, you have to static assert on each type individually; you can't put some syntax in the default move constructor to make attempts to move B fail.
The reason for that has to do in part with backwards compatibility. Think about all the pre-C++11 code that declared user-defined copy constructors. By the rules of move constructor generation in C++11, all of them would have deleted move constructors. Which means that any code of the form T t = FuncReturningTByValue(); would fail, even though it worked just fine in C++98/03 by calling the copy constructor. So the move-by-copy issue worked around this by making these copy instead of moving if the move constructor could not be generated.
But since = default means "do what you would normally do", it also includes this special overload resolution behavior that ignores the implicitly deleted move constructor.
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.
I'm struggling to basically understand how the assignment operator works, or perhaps I haven't defined the right one, or I've come upon some C++ gotcha; not sure.
I have a class like this:
class A
{
public:
A();
A(const A &rhs);
//some other ctors
//and functions here
A& operator=(A rhs);
A create_half();
double* m_data;
};
Here is the body of create_half; it appears to be relevant to the situation
A A::create_half(){
//construct a new A
A mya;
//do stuff to mya here,
//and then return the new A
return mya;
}
Here is the copy constructor, at least a shortened version of it. This is the code that is skipped due to standard -- at least if this is what's truly going on.
A::A(const A& rhs):
m_data(new double[1])
{
*m_data = *(rhs.m_data);
//or something similar, essentially
//a deep copy
}
Then in the code I try to write something like this
A a_instance;
//do stuff to a_instance
//this doesn't call any = operator or
//copy c-tor I try to define, why?
A b_instance = A.create_half();
//but this works fine
//the = operator I have def'd is called
A b_instance;
b_instance = A.create_half();
The line that doesn't work as I want it to seems to do a default copy (ie, b_instance.m_data is set to a_instance.m_data) even though neither my copy c-tor now my assignment operator contain this operation.
For the problematic line, I'm not able to step into the assignment operator; that's why I think I have the wrong signature, or something similar.
Any ideas?
EDIT: Sorry for misleading you guys -- no class B exists.
EDIT AGAIN: I've accepted an answer, and I've added some detail to the snippets above to try to elucidate what is really going on.
Since the instance of A created by create_half() isn't bound to a reference, the standard allows elision of the copy constructor, which, critically, allocates new memory for b_instance, and does the deep copy.
That seems pretty bold, to me! I don't think I'll see an answer, since this edit is sometime after the initial posting, but what would be a better design pattern here?
This
A a_instance();
is a function declaration that has no parameters and has return type A.
This statement
B b_instance = A.create_half();
syntactically is invalid. At least it should be written as
B b_instance = A().create_half();
In this statement an object of type B is created from an object of type A. I can say nothing whether this statement is correct because you did not show the class B definition.
This statement is also invalid
b_instance = A.create_half();
There must be
b_instance = A().create_half();
There is used indeed an assignment operator (maybe even the copy assignment operator provided that class B has a conversion constructor or class A has a conversion function) but for class B. You did not show how you defined class B. So again I can say nothing what is wrong with this assignment operator.
As for the signature of the copy assignment operator of class A
A& operator=(A rhs);
then it is valid though it would be better if you declared it as
A& operator=( const A &rhs );
EDIT: As you changed your original post then I need to append my message relative to your question in the following code snippet
//this doesn't call any = operator or
//copy c-tor I try to define, why?
A b_instance = A.create_half();
According to the C++ Standard
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.
One of the "certin criteria" is
— 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
My current implementation uses lots of copy constructors with this syntax
MyClass::Myclass(Myclass* my_class)
Is it really (functionnaly) different from
MyClass::MyClass(const MyClass& my_class)
and why?
I was adviced that first solution was not a true copy constructor. However, making the change implies quite a lot of refactoring.
Thanks!!!
It's different in the sense that the first isn't a copy constructor, but a conversion constructor. It converts from a MyClass* to a MyClass.
By definition, a copy-constructor has one of the following signatures:
MyClass(MyClass& my_class)
MyClass(const MyClass& my_class)
//....
//combination of cv-qualifiers and other arguments that have default values
12.8. Copying class objects
2) A non-template constructor for class X is a copy constructor if its
first parameter is of type X&, const X&, volatile X& or const volatile
X&, and either there are no other parameters or else all other
parameters have default arguments (8.3.6).113) [ Example: X::X(const
X&) and X::X(X&,int=1) are copy constructors.
EDIT: you seem to be confusing the two.
Say you have:
struct A
{
A();
A(A* other);
A(const A& other);
};
A a; //uses default constructor
A b(a); //uses copy constructor
A c(&a); //uses conversion constructor
They serve different purposes alltogether.
The first version is not a copy constructor. Simple as that. It's just another constructor.
A copy constructor for a class X must have signature (X &, ...) or (X const &, ...) or (X volatile &, ...) or (X const volatile &, ...), where all arguments but the first have default values if they are present (and it must not be a template).
That said, you should think very carefully about why you're violating the Rule of Zero: Most well-designed classes shouldn't have any user-defined copy-constructor, copy-assignment operator or destructor at all, and instead rely on well-designed members. The fact that your current constructor takes a pointer makes me wonder if your code behaves correctly for something like MyClass x = y; — worth checking.
Certain language constructs call for a copy constructor:
passing by value
returning by value
copy-style initialization (although the copy is often elided in that case)
If the language calls for a copy, and you have provided a copy constructor, then it can be used (assuming the cv-qualifications are OK, of course).
Since you have not provided a copy constructor, you will get the compiler-generated copy constructor instead. This works by copying each data member, even if that's not the right thing to do for your class. For example if your not-a-copy-constructor explicitly does any deep copies or allocates resources, then you need to suppress the compiler-generated copy.
If the compiler-generated copy works for your class, then your not-a-copy-constructor is mostly harmless. I don't think it's a particularly good idea, though:
void some_function(const MyClass &foo);
MyClass *ptr = new MyClass();
const MyClass *ptr2 = ptr;
some_function(ptr); // compiles, but copies *ptr and uses the copy
MyClass bar(ptr2); // doesn't compile -- requires pointer-to-non-const
Even assuming that the compiler-generated copy is no good for your class, making the necessary change need not require a lot of refactoring. Suppose that your not-a-constructor doesn't actually modify the object pointed to by its argument, so after fixing the signature you have:
MyClass::MyClass(const MyClass* my_class) {
// maybe treat null pointer specially
// do stuff here
}
You need:
MyClass::MyClass(const MyClass& my_class) {
do_stuff(my_class);
}
MyClass::MyClass(const MyClass* my_class) {
// maybe treat null pointer specially
do_stuff(*my_class);
}
MyClass::do_stuff(const MyClass& my_class) {
// do stuff here
}
You also need to copy any initializer list from the old constructor to the new one, and modify it for the fact that my_class isn't a pointer in the new one.
Removing the old constructor might require a lot of refactoring, since you have to edit any code that uses it. You don't have to remove the old constructor in order to fix any problems with the default copy constructor, though.
The first example is not a copy constructor. This means that when you provide it, the compiler still provides you with a default copy constructor with signature equivalent to
MyClass(const MyClass& my_class);
If you are doing something special with your constructor, and the compiler provided copy constructor does not follow that logic, you should either implement a copy constructor or find a way to disable it.
Why would I want to use a copy constructor instead of a conversion constructor?
It can be problematic if you give MyClass objects to some code that expect your copy-constructor to be valid.
This is the case for STL containers. For instance, if you use a std::vector<MyClass>, you must be aware that vectors are allowed to move elements around for reallocation using their copy constructors.
The default constructor provided by the compiler will perform a shallow copy, calling copy constructors of every attributes, making simple copies for base type like pointers. If you want some form of deep copy you will have to properly rewrite the copy constructor of MyClass