Disallowing Inaccessible Operators From Trivially Copyable


Summary

This proposal aims to solve a serious flaw in the definition of the trivially copyable concept. Presently, classes with deleted or inaccessible copy/move constructors, copy/move assignment operators and destructor are allowed to be trivially copyable, which might seem counterintuitive.

The Problem

When a class is trivially copyable, it is expected that this class can be copied without involving any operators, by using for example std::memcpy to copy from one object to another. This can be highly advantageous when copying large arrays, as the whole array can be copied in just one call to std::memcpy, typically resulting in improved performance.

A generic algorithm can be written thus that elects to copy a given array either by calling the copy constructor on each element or by copying the whole array with std::memcpy, depending on the value of std::is_trivially_copyable<T>::value, where T is the array element type.

However there is a problem. From the definition of trivially copyable ([class] paragraph 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).

According to this definition, a class with all deleted or inaccessible copy/move constructors, copy/move assignment operators and destructor is trivially copyable.

This is problematic, because idiomatically it is expected that a class with all deleted copy constructors/operators cannot be copied, that a class with all deleted move constructors/operators cannot be moved and that a class with a deleted destructor cannot be destroyed.

But when using an array of elements of such a class with the generic algorithm just hypothesized, the std::memcpy path will be selected and it will not fail to compile, creating copies of objects which the user probably intended that cannot be copied, and in the process destroying the elements of the destination array, which the user probably intended that cannot be destroyed.

The Solution

Strike the current definition of trivially copyable from [class] paragraph 6, and redefine it as:

A trivially copyable class is a class that:
— has no non-trivial copy constructors, move constructors,
  copy assignment operators and move assignment operators (13.5.3, 12.8),

— either has a trivial, non-deleted and accessible
  copy constructor or move constructor (12.8),

— either has a trivial, non-deleted and accessible
  copy assignment operator or move assignment operator (13.5.3, 12.8), and

— has a trivial, non-deleted and accessible destructor (12.4).

Acknowledgements

Ville Voutilainen for discussing the problem on std-discussion and helping the author understand the nuances regarding deleted trivial copy constructors.