When running this simple program, different behaviour is observed depending on the compiler.
It prints true
when compiled by GCC 11.2, and false
when compiled by MSVC 19.29.30137 with the (both are the latest release as of today).
#include <type_traits>
#include <iostream>
struct S {
int a;
S() = delete;
S(S const &) = delete;
S(S &&) = delete;
S &operator=(S const &) = delete;
S &operator=(S &&) = delete;
~S() = delete;
};
int main() {
std::cout << std::boolalpha;
std::cout << std::is_trivially_copyable_v<S>;
}
Relevant quotes (from the latest C++23 working draft N4901):
Given 20.15.5.4 [meta.unary.prop], std::is_trivially_copyable_v<T>
is defined to be true if T is a trivially copyable type
as defined at 6.8.1/9 [basic.types.general]:
Arithmetic types (6.8.2), enumeration types, pointer types, pointer-to-member types (6.8.3), std::nullptr_t, and cv-qualified (6.8.4) versions of these types are collectively called scalar types. Scalar types, trivially copyable class types (11.2), arrays of such types, and cv-qualified versions of these types are collectively called trivially copyable types.
where trivially copyable class types
is defined at 11.2/1 [class.prop]:
1 A trivially copyable class is a class:
— that has at least one eligible copy constructor, move constructor, copy assignment operator, or move assignment operator (11.4.4, 11.4.5.3, 11.4.6),
— where each eligible copy constructor, move constructor, copy assignment operator, and move assignment operator is trivial, and
— that has a trivial, non-deleted destructor (11.4.7).
Eligible (11.4.4 [special]):
1 Default constructors (11.4.5.2), copy constructors, move constructors (11.4.5.3), copy assignment operators, move assignment operators (11.4.6), and prospective destructors (11.4.7) are special member functions.
6 An eligible special member function is a special member function for which:
— the function is not deleted,
— the associated constraints (13.5), if any, are satisfied, and
— no special member function of the same kind is more constrained
trivial
for these functions (as defined at 11.4.5.3/11 [class.copy.ctor], 11.4.6/9 [class.copy.assign], 11.4.7/8 [class.dtor]) generally means:
- the function is not user-provided.
- there's nothing virtual to the class
- each non-static data member has the relevant trivial function
As per 9.5.2/5 [dcl.fct.def.default], the deleted functions in the provided program aren't user-provided:
... A function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. ...
If my understanding is correct, struct S
has deleted
special member functions
making them non-eligible
, which doesn't meet the requirements for a trivially copyable class type
and trivially copyable type
. Therefore the conforming behaviour is MSVC's. Is this correct?
GCC and Clang report that S
is trivially copyable in C++11 through C++23 standard modes. MSVC reports that S
is not trivially copyable in C++14 through C++20 standard modes.
N3337 (~ C++11) and N4140 (~ C++14) say:
A trivially copyable class is a class that:
- has no non-trivial copy constructors,
- has no non-trivial move constructors,
- has no non-trivial copy assignment operators,
- has no non-trivial move assignment operators, and
- has a trivial destructor.
By this definition, S
is trivially copyable.
N4659 (~ C++17) says:
A trivially copyable class is a class:
- where each copy constructor, move constructor, copy assignment operator, and move assignment operator is either deleted or trivial,
- that has at least one non-deleted copy constructor, move constructor, copy assignment operator, or move assignment operator, and
- that has a trivial, non-deleted destructor
By this definition, S
is not trivially copyable.
N4860 (~ C++20) says:
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.
By this definition, S
is not trivially copyable.
Thus, as published, S
was trivally copyable in C++11 and C++14, but not in C++17 and C++20.
The change was adopted from DR 1734 in February 2016. Implementors generally treat DRs as though they apply to all prior language standards by convention. Thus, by the published standard for C++11 and C++14, S
was trivially copyable, and by convention, newer compiler versions might choose to treat S
as not trivially copyable in C++11 and C++14 modes. Thus, all compilers could be said to be correct for C++11 and C++14.
For C++17 and beyond, S
is unambiguously not trivially copyable so GCC and Clang are incorrect. This is GCC bug #96288 and LLVM bug #39050
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With