Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is is_trivially_copyable_v different in GCC and MSVC?

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?

like image 292
La Creatura Avatar asked Dec 07 '21 21:12

La Creatura


1 Answers

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

like image 64
Jeff Garrett Avatar answered Jan 11 '23 14:01

Jeff Garrett