I want my C++ type to expose a move-only interface, therefore I declared its copy constructor and copy assignment as deleted. Nonetheless, the move constructor and move assignment are trivial, and the destructor is also trivial, therefore it is considered as "trivially_copyable" for the C++ standard. This is a problem, because copying an object of this type really is semantically wrong.
A possible solution is to user-define an empty destructor that does nothing. This makes the type not "trivially_copyable", but as a consequence the type is not longer "trivial for the purposes of calls" for the Itanium ABI, which can have negative performance implications.
Is there a way to achieve all these goals at the same time? The type should have trivial move-constructor, move-assignment and destructor. The type should not be trivially copyable. The type should be trivial for the purpose of calls for the Itanium ABI.
https://itanium-cxx-abi.github.io/cxx-abi/abi.html#non-trivial
A type is considered non-trivial for the purposes of calls if:
- it has a non-trivial copy constructor, move constructor, or destructor, or
- all of its copy and move constructors are deleted.
A trivially copyable class is a class:
- that has at least one eligible copy constructor, move constructor, copy assignment operator, or move assignment operator,
- where each eligible copy constructor, move constructor, copy assignment operator, and move assignment operator is trivial, and
- that has a trivial, non-deleted destructor.
So all the copy/move constructors and the destructor have to remain non-trivial, so you can only make it not trivially copyable with a copy or move assignment operator. Since you want the move assignment operator to be trivial, that leaves a copy assignment operator:
struct T {
T(T&&) = default;
T(const T&) = delete;
T& operator=(T&&) = default;
T& operator=(const T&) = delete;
~T() = default;
private:
[[deprecated("To ensure T is not trivially copyable; should not be used")]] void operator=(const volatile T&);
};
This can also be solved with [[clang::trivial_abi]]
:
struct [[clang::trivial_abi]] T {
constexpr T(T&&) noexcept;
T(const T&) = delete;
T& operator=(T&&) = default;
T& operator=(const T&) = delete;
~T() = default;
};
constexpr T::T(T&&) noexcept = default;
But that is only available with Clang (which modifies the definition of "non-trivial for the purposes of calls" to exclude classes with this attribute)