Can C++ classes with deleted methods be trivially copyable? - c++

I want class B to inherit all but a few methods of class A (which is assumed to be trivially copyable), and still be trivially copyable. In C++11 I can delete methods. Take for example:
class A { // trivially copyable
// private stuff here
public:
A& operator += (const A&);
// other public stuff here
};
class B: public A {
public:
B& operator += (const A&) = delete;
};
Is B trivially copyable? I know there are issues regarding the deletion of special methods, but the compound assignment is not a special method (right?).

Yes, B is trivially copyable - regardless of what you do to non-special member functions.
N3337, §9/6:
A trivially copyable class is a class that: — has no non-trivial
copy constructors (12.8), — has no non-trivial move constructors
(12.8), — has no non-trivial copy assignment operators (13.5.3, 12.8),
— has no non-trivial move assignment operators (13.5.3, 12.8), and —
has a trivial destructor (12.4).
but the compound assignment is not a special method (right?)
No, it's not.
N3337, §12/1:
The default constructor (12.1), copy constructor and copy assignment
operator (12.8), move constructor and move assignment operator (12.8),
and destructor (12.4) are special member functions.

I think you're on the right track--if A is trivially copyable and B is derived from A and simply deletes some regular methods (or operators), B will be trivially copyable too.

Related

Implicit move constructor and assignment operator

What does it mean that implicit move constructor does a member-wise move and implicit move assignment operator a member-wise assignment?
From https://en.cppreference.com/w/cpp/language/move_constructor:
For non-union class types (class and struct), the move constructor
performs full member-wise move of the object's bases and non-static
members, in their initialization order, using direct initialization
with an xvalue argument. If this satisfies the requirements of a
constexpr constructor, the generated move constructor is constexpr.
From https://en.cppreference.com/w/cpp/language/move_assignment:
For non-union class types (class and struct), the move assignment
operator performs full member-wise move assignment of the object's
direct bases and immediate non-static members, in their declaration
order, using built-in assignment for the scalars, memberwise
move-assignment for arrays, and move assignment operator for class
types (called non-virtually).
Will the implicit members look like this for the following exemplary class template:
template<class T>
class Holder {
public:
Holder(int size) : m_size(size) { m_data = new T[m_size]; }
Holder(Holder && other) :
m_size(std::move(other.m_size)),
m_data(std::move(other.m_data))
{}
Holder& operator=(Holder && other) {
if(this == &other) return *this;
m_data = std::move(other.m_data);
m_size = std::move(other.m_size);
return *this;
}
~Holder() { delete [] m_data; }
private:
T* m_data;
int m_size;
};
What's more, what will the std::move() in the above example transfer the resources?
If you look further down you linked page, you will see that your classes compiler generated move constructor (and move assignment operator) will actually be Trivial:
Trivial move constructor
The move constructor for class T is trivial if all of the following is true:
it is not user-provided (meaning, it is implicitly-defined or defaulted);
T has no virtual member functions;
T has no virtual base classes
the move constructor selected for every direct base of T is trivial;
the move constructor selected for every non-static class type (or array of class type) member of T is trivial;
A trivial move constructor is a constructor that performs the same action as the trivial copy constructor, that is, makes a copy of the
object representation as if by std::memmove. All data types
compatible with the C language (POD types) are trivially movable.
(Emphasis mine)
The two member variables are POD types and therefore are trivially movable. Since your class is not virtual and it holds no non-trivial members it is therefore trivial and all the data members will be copied. As mentioned in the comments, this will lead to double deleting your pointer and UB.
Since this is the case, you need to implement your move semantics properly, by taking ownership of the moved objects pointer and setting it to nullptr. Or better yet, just use std::vector or even std::unique_ptr.

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.

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.

Are move constructors/assignment operators generated for derived classes

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.

When does the compiler provide definitions for the special members of a class?

I know that when I define an empty class and provide no declarations at all, the compiler will provide definitions for the default and copy constructor, destructor and copy assignment operator.
What are the rules for that? When does the compiler not provide a, say, copy constructor? What about the move constructor and move assignment operator?
(Example: The compiler will not provide definitions for any assignment operator if my class has a reference member like int&. When else will something like this happen?)
Edit: In C++11 it's more complicated than implicitly declared or not. They can either be implicitly declared and defaulted, implicitly declared and deleted, or undeclared. Read this to distinguish the latter 2. The following information isn't entirely correct since it doesn't distinguish declared and deleted vs undeclared.
The following is a work in progress. (?) indicates I would like to clarify or quantify the statement.
Special Member Functions §12/1
The implementation will implicitly declare these member functions for some class types when the user does not explicitly declare them:
default constructor
copy constructor
copy assignment operator
move constructor
move assignment operator
destructor
A special member function will NOT be implicitly declared if the type has a...
Default Constructor §12.1/5
Copy Constructor §12.8/8, §12.8/12
user-declared move constructor
user-declared move assignment operator
non-static data member of rvalue reference type
variant member with a non-trivial copy constructor and is a union-like class (?)
non-static data member of type (or array thereof) that cannot be copied
direct or virtual base class without accessible copy constructor
*Such an implicit declaration is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor (?)
Copy Assignment Operator §12.8/19, §12.8/24
user-declared move constructor
user-declared move assignment operator
a variant member with a non-trivial copy assignment operator and is a union-like class (?)
non-static data member of const non-class type (or array thereof)
non-static data member of reference type
non-static data member with inaccessible copy assignment operator
direct or virtual base class with inaccessible copy assignment operator
*Such implicit declaration is deprecated if the class has a user-declared copy constructor or a user-declared destructor (?)
Move Constructor §12.8/10, §12.8/12
user-declared copy constructor
user-declared copy assignment operator
user-declared move assignment operator
user-declared destructor
move constructor would not be implicitly defined as deleted (?)
variant member with a non-trivial move constructor and is a union-like class (?)
non-static data member of type (or array thereof) that cannot be moved
non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable
direct or virtual base class without accessible move constructor
Move Assignment Operator §12.8/21, §12.8/24
user-declared copy constructor
user-declared move constructor
user-declared copy assignment operator
user-declared destructor
move assignment operator would not be implicitly defined as deleted (?)
a variant member with a non-trivial move assignment operator and is a union-like class (?)
non-static data member of const non-class type (or array thereof)
non-static data member of reference type
non-static data member with inaccessible move assignment operator and is not trivially copyable
direct or virtual base class with inaccessible move assignment operator and is not trivially copyable
Destructor §12.4/4