14

Code is like:

#include <iostream>
#include <type_traits>

class A
{
    A() = default;
    A(const A&) = default;
    A(A&&) = default;
    A& operator=(const A&) = default;
    A& operator=(A&&) = default;
    ~A() = default;
    int a;
};

int main()
{
    std::cout << std::boolalpha <<
        std::is_trivially_copy_assignable_v<A> << " " <<
        std::is_trivially_copy_constructible_v<A> << " " <<
        std::is_trivially_move_assignable_v<A> << " " <<
        std::is_trivially_move_constructible_v<A> << " " <<
        std::is_trivially_destructible_v<A> << " " <<
        std::is_trivially_copyable_v<A> << "\n";
    return 0;
}

And the output is false false false false false true. But according to cppreference:

A trivially copyable class is a class that

  • has at least one eligible copy constructor, move constructor, copy assignment operator, or move assignment operator,
  • each eligible copy constructor is trivial
  • each eligible move constructor is trivial
  • each eligible copy assignment operator is trivial
  • each eligible move assignment operator is trivial, and
  • has a non-deleted trivial destructor.

So from the first four boolean outputs, none of the copy constructor, move constructor, copy assignment operator and move assignment operator is trivial, so it's supposed to be non-trivially-copyable; However, the last output boolean is true.

Note: You can make ~A() = default and A() = default public so that you can create objects, but the core question is still the same.

Jason
  • 36,170
  • 5
  • 26
  • 60
o_oTurtle
  • 1,091
  • 3
  • 12
  • Just for completeness: Which compiler and standard version did you use? – Sebastian Feb 13 '23 at 08:56
  • 2
    @Sebastian I've tried gcc 12.2 and msvc 19.29. The standard version is C++20 – o_oTurtle Feb 13 '23 at 08:57
  • @ALX23z Could you specify, how this requirement fits to the cited definition / standard wording? – Sebastian Feb 13 '23 at 09:00
  • @Sebastian you entered into a gray area. Something noone ever bothered checking as no one would ever use it. By making most of constructors/assignments private, the class is effectively non-usable. I don't think that writers of the standart considered such cases when they wrote it. – ALX23z Feb 13 '23 at 09:03
  • _from the first four boolean outputs, none of the copy constructor, move constructor, copy assignment operator and move assignment operator is trivial_ This is not what `is_trivially...` traits check. – Language Lawyer Feb 13 '23 at 09:30
  • 2
    It seems the issue is caused by initially wrong wording in the standard that was addressed in [CWG 1734](https://cplusplus.github.io/CWG/issues/1734.html) but it seems it is still not implemented in both [gcc](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98936) and [clang](https://bugs.llvm.org/show_bug.cgi?id=39050), and clang discussion mentions possible abi issues in case of the fix. – dewaffled Feb 13 '23 at 09:38
  • @LanguageLawyer Err...It's quite oblique if a class's copy ctor is trivial while `std::is_trivially_copy_constructible_v` is false. – o_oTurtle Feb 13 '23 at 09:43
  • 4
    _It's quite oblique if a class's copy ctor is trivial while `std::is_trivially_copy_constructible_v` is false_ Why? It does not say `std::has_trivial_copy_constructor_v`, after all. – Language Lawyer Feb 13 '23 at 09:45

3 Answers3

18

The question is not can an instance of this class actually be copied?

std::is_trivially_copyable answers

if you copied this instance with a bytewise copy, would the result be the same as copying using a copy operator?

Andy Dent
  • 17,578
  • 6
  • 88
  • 115
10

std::is_trivially_copy_constructible_v<T> doesn't check whether T has a trivial copy constructor, which is what trivially-copyable is based on.

Instead it checks whether a definition of a variable of the form

T t(u);

where u is a variable of type const T, would be well-formed in a context unrelated to T and that the construction of the variable would use only trivial functions. This includes a check for accessibility and a check for a non-deleted accesible destructor.

The same applies in similar form to the other _constructible_ and _assignable_ traits. These traits are not directly related to is_trivially_copyable. The is_trivially_copyable trait tells you whether you can use memcpy to copy objects of the type. It doesn't tell you whether copy/move-construction/assignment will be possible and trivial, which is what the other traits are for.

user17732522
  • 53,019
  • 2
  • 56
  • 105
3

This is because the class A has an eligible move constructor that is trivial. Moreover, it also has a trivial non-deleted destructor and so class A satisfies all the requirement quoted below from trivially copyable class:

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 (11.4.7).

(emphasis mine)

Note the emphasis on at least which means that class A satisfied all of the 3 above requirements.


The destructor is trivial as it is not user-provided and satisfied all 3 conditions given below:

A destructor is trivial if it is not user-provided and if:

  • the destructor is not virtual

  • all of the direct base classes of its class have trivial destructors, and

  • for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.


Similarly, the move constructor is also trivial which is one of the requirements(the second one to be specific) for a trivially copyable class.

A copy/move constructor for class X is trivial if it is not user-provided and if:

  • class X has no virtual functions (11.7.3) and no virtual base classes (11.7.2), and

  • the constructor selected to copy/move each direct base class subobject is trivial, and

  • for each non-static data member of X that is of class type (or array thereof), the constructor selected to copy/move that member is trivial;

Jason
  • 36,170
  • 5
  • 26
  • 60
  • The problem is that, say, it reports that the destructor is not trivial according to type traits. So some of the reported values are definitely wrong. – ALX23z Feb 13 '23 at 09:28
  • @ALX23z I think you've mis-understood what first four traits mean. – Jason Feb 13 '23 at 09:33