Are move constructors/assignment operators generated for derived classes - c++

If class X is derived from class Y and class Y has any of:
A user-declared copy constructor,
A user-declared copy assignment operator,
A user-declared destructor
A user-declared move constructor,
A user-declared move assignment operator,
Will a move constructor and move assignment operator be implicitly defaulted for class X providing it declares none of the above?
e.g.
struct Y
{
virtual ~Y() {}
// .... stuff
};
struct X : public Y
{
// ... stuff but no destructor,
// no copy/move assignment operator
// no copy/move constructor
// will X have a default move constructor / assignment operator?
};
I am currently using gcc, but I am mainly interested in what the correct behaviour should be (as opposed to whether or not a particular compiler is standards compliant)..

As far as the derived class is concerned, the fact that an attribute or a base class has user-defined or implicit special functions does not matter. All that matter is their presence.
Compliant C++11 compilers should automatically provide the move constructors and assignment operators for the struct and class, if possible (which is clearly defined in the Standard), even though only classes with dynamically allocated buffers will really benefit from it (moving an int is just copying it).
Therefore, if your class embeds a std::string or a std::unique_ptr (for example), then its move constructor will call the embedded string or unique_ptr move constructor and it will be efficient... for free.
Simply changing the compilation mode should thus slightly improve performance.

Yes, they are, in §12.8.9:
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.
and §12.8.10
The implicitly-declared move constructor for class X will have the form
X::X(X&&)
Similarly, for move assignment operators in 12.8.20:
If the definition of a class X does not explicitly declare a move assignment operator, 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 move constructor,
X does not have a user-declared copy assignment operator,
X does not have a user-declared destructor, and
the move assignment operator would not be implicitly defined as deleted.
Base classes don't come into it directly.

Related

Is an empty constructor with initializer list considered trivial?

Is the following constructor considered trivial?
struct A
{
A() : a(nullptr) {}
private:
int* a;
};
These examples makes me a little confused. With c++11 this should also be possible:
struct A
{
private:
int* a{nullptr};
};
and a should be properly initialized to nullptr. Here I did not define the constructor, but it should have the same form as the first implementation. Is either of these classes considered trivial?
The purpose of me asking is if I can expect move/copy constructors and assignment operators being automatically generated.
Is the following constructor considered trivial?
A() : a(nullptr) {}
No, because it is user-defined.
struct A
{
private:
int* a{nullptr};
};
No, because it has brace initializer for a non-static member.
According to the standard (emphasis mine):
12.1 Constructors....
A default constructor for a class X is a constructor of class X that can be called without an argument. If
there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted (8.40).....
A default constructor is trivial if it is not user-provided and if:....
— no non-static data member of its class has a brace-or-equal-initializer, and....
Otherwise, the default constructor is non-trivial.
The purpose of me asking is if I can expect move/copy constructors and assignment operators being automatically generated.
As #M.M and #NicolBolas commented, generation of these constructors and operators are not impacted by existence of the trivial constructor.
The rules are a bit complicated and not very consistent.
The copy constructor is generated only if there is no explicitly declared one. (And it is deleted if the move constructor or the move assignment operator is declared.)
Similarly, the copy assignment operator is generated only if there is no explicitly declared one. (And again, it is deleted if the move constructor or the move assignment operator is declared.)
The move constructor is generated only if there are no explicitly declared move constructor, move operator, copy constructor, copy assignment and destructor.
The same rule is for the move assignment operator.

What's with the copy-constructor if the class contains a user-declared destructor?

The Standard in section 12.8/7 says:
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. Thus, for the class definition
struct X {
X(const X&, int);
};
a copy constructor is implicitly-declared. If the user-declared
constructor is later defined as
X::X(const X& x, int i =0) { /∗ ... ∗/ }
I can't get the point of that The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor. In the example the Standard neither provides a user-declared copy assignment operator nor a destructor. What will happen if we declare a destructor or a copy assignment operator? I've tried to do that as follows:
struct A
{
~A(){ };
};
A::A(const A&){ }; //error
int main(){ }
DEMO
but in the example we still have the implicitly-declared copy constructor. What does that rule actual mean?
I thought that if we write the following:
struct A
{
A(){ };
A(const A&&){ };
~A(){ };
};
A a;
A t = a; //error: call to implicitly-deleted copy constructor of 'A'
int main()
{
}
DEMO
the copy-constructor won't explicitly deleted. But thats's not the case.
This deprecation basically incorporates the Rule of Three (Five). If a user-declared copy assignment operator or destructor is provided, the fact that the copy constructor is defined as defaulted (and not as deleted) is deprecated. That should prevent you from depending upon such an implicitly declared copy constructor in future.
In the example the Standard provides neither copy assignment nor
destructor are user-decalred.
The example has nothing to do with the deprecation.
I've tryied to do that as follows: […] but in the example we still have the impliclty-declared copy
constructor.
You cannot define an implicitly declared copy constructor - because it's already defined, by = default (no pun intended). You would have to declare it yourself first.
I thought that If we wirte the following: […] the copy-constructor won't explicitly deleted. But it's not true.
You quoted the rule that explicitly specifies that the copy constructor will be implicitly defined as deleted if a move constructor is declared:
If the class definition declares a move constructor or move
assignment operator, the implicitly declared copy constructor is
defined as deleted;
Clearly,
A(const A&&){ }
Is a move constructor according to [class.copy]/3. If you removed this move constructor then your example compiles, although it uses said deprecated feature.
Deprecated normally means that something will work, but that it is frowned upon and may not work in the future. I think the standard is saying that if you create a user-declared copy assignment operator or a user-declared destructor it will still create a default copy constructor (if you haven't) - but it may not in the future. So they want you to create your own copy constructor now, if you have one of the other two, and in the future they may force you to.

Will move constructor and move assignment be generated if I default the copy constructor?

I am a little confused about how best to define a copyable but not moveable class. It seems to me that deleting the move constructor is a bad idea because then I couldn't construct from a temporary. On the other hand I don't want the compiler to implicitly generate a move constructor for me that may be wrong or may lead to unexpected behavior. My question is whether the compiler is allowed to implicitly generate a move constructor for the following class:
class C {
public:
int i_;
C(const C&) = default;
}
[class.copy]/9 after CWG 1402:
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, and
X does not have a user-declared destructor.
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
[and ...]
Since the copy constructor is user-declared (even though it is defaulted, which means it is not user-provided), a move constructor will not be implicitly declared as defaulted.
You're right that if you were to explicitly delete the move constructor, you would not be able to construct from temporaries because the deleted move constructor would be chosen by overload resolution. So the approach you have used for a copyable but not moveable class is the right one.
Relative to your example the compiler will implicitly declare a move constructor because the first declaration of your copy constructor is the default definition. In this case the copy constructor is considered as implicitly defined. But if you would change your class definition the following way
class C {
public:
int i_;
C(const C&);
};
C::C( const C & ) = default;
then in this case the move constructor would not be generated by the compiler becasue the copy constructor is explicitly declared by the user.

Is it enough to delete operator=(Type type)?

Is the following enough (from a best-practice perspective) for a nonmovable type?
class A
{
A(const A&) = delete;
A(A&&) = delete;
A& operator=(A) = delete;
public:
A();
};
Or do I have to delete the copy/move assignment operators separately? Also is a destructor required here?
Yes, declaring the copy constructor and copy assignment operators as deleted is enough. Since you are declaring a copy constructor and copy assignment operator the move constructor and move assignment operator will not be automatically generated. You do not need to explicitly declare them deleted.
From §12.8/9 (Emphasis added)
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.
From §12.8/20 (Emphasis added)
If the definition of a class X does not explicitly declare a move assignment operator, 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 move constructor,
— X does not have a user-declared copy assignment operator,
— X does not have a user-declared destructor, and
— the move assignment operator would not be implicitly defined as deleted.
According to [class.copy]/17 operator=(A) is a valid copy assignment operator, so yes, declaring it deleted is sufficient to suppress the implicit move assignment operator and therefore along with the deleted copy constructor will make the class non-copyable and non-movable.
Your class is more complicated than necessary though, all you need is:
class A
{
A(const A&) = delete;
A& operator=(A) = delete;
public:
A();
};
The user-declared copy constructor suppresses the implicit move constructor, and the user-declared copy assignment operator suppresses the implicit move assignment operator.
However, the operator=(A) form is un-idiomatic, so I would tend to use operator=(const A&) = delete anyway. It has the same effect.
N.B. there is no reason to declare deleted functions private, and in fact it results in much worse diagnostics. Public and deleted works better in my experience.
Also is a destructor required here?
Required for what? It depends what your default constructor does.

Implicitly generate move constructor

Is there any way a move constructor for a move-only class can be implicitly generated? Consider a class like this:
class moveable_only
{
unique_ptr<int> p_;
};
moveable_only m;
foo(std::move(m));
This doesn't compile, because the implicitly declared copy constructor cannot copy p_. (12.8/7)
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).
Now consider this:
class moveable_only
{
unique_ptr<int> p_;
moveable_only(const moveable_only&);
moveable_only& operator = (const moveable_only&);
};
moveable_only m;
foo(std::move(m));
This doesn't compile as well, because of 12.8/9
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.
This doesn't compile, because the implicitly declared copy constructor cannot copy p_. (12.8/7)
There is no need for a copy constructor. This does not compile because your compiler does not seem to generate a move constructor automatically, which it should.
There is no way around that other than implementing it yourself or updating the compiler.