15

Built with this online compiler, the following code:

#include <iostream>
#include <type_traits>
#include <tuple>

int main() {
    std::cout << std::is_trivially_copyable<std::tuple<int>>::value << std::endl;
    std::cout << std::is_trivially_copyable<std::pair<int, int>>::value << std::endl;

    std::cout << std::is_trivial<std::tuple<int>>::value << std::endl;
    std::cout << std::is_trivial<std::pair<int, int>>::value << std::endl;
    return 0;
}

outputs:

0
0
0
0

I'm getting the same results with Visual Studio 2015.

Why is that the case? Is there a valid reason an std::tuple of POD types, let alone a simple std::pair, couldn't be trivially copyable? I presume their implementations provide some custom assignment operators, but how would they be different from the default versions generated by the compiler?

Pythagoras of Samos
  • 3,051
  • 5
  • 29
  • 51
  • 1
    http://stackoverflow.com/questions/36625317/error-cannot-pass-objects-of-non-trivially-copyable-type-through implies that the standard doesn't require it, so implementations don't bother – happydave Aug 05 '16 at 02:06

2 Answers2

15

The thing that trips pair up as far as trivial copyability is concerned is that the standard does not require that the copy/move assignment operators be trivial. The standard explicitly declares that the copy/move constructors are defaulted, but not so for the assignments. An implementation could default them as well, but the standard does not require it.

There's no really good reason why the standard doesn't require it. But it doesn't.

For tuple, things are a lot more complicated. Many tuple implementations are based on having a storage buffer of the right size/alignment, and using placement new to construct the individual members within that buffer. That's all fine and good, but such a type has to implement a manual copy/move constructor, since it must call the copy/move constructor of each type. Even if it knew that they were all trivially copyable and copied them via memcpy, that's still a manual operation. And that disqualifies it from trivial copyability.

Now, there are implementations of tuple which could be trivially copyable if the types are trivially copyable. But there is no requirement to implement them that way. And it would complicate tuple implementations tremendously to do require them to implement themselves one way if all the types are trivially copyable, and implement them in a different way otherwise.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • tuple( const tuple& other ) = default; (12) (since C++11) tuple( tuple&& other ) = default; (13) (since C++11) – user877329 Jan 06 '23 at 11:52
  • @user877329: ... so? If the tuple is implemented through base classes of some sort, that can work. But many tuple implementations work by creating a buffer within the tuple and explicitly constructing the elements within it. You can't `default` your copy/move constructors/assignment in those cases. – Nicol Bolas Jan 06 '23 at 14:32
  • How do you deal with constexpr then? Especially before C++20. – user877329 Jan 07 '23 at 18:05
  • @user877329: Whether something can be `constexpr` has nothing to do with trivial copyability. C++14 made `tuple` `constexpr`-capable. – Nicol Bolas Jan 07 '23 at 18:17
  • I thought about your mentioning of placement new stuff. – user877329 Jan 07 '23 at 19:02
  • @user877329: Standard library implementations are not limited to what you can do. They could have had some constexpr-style `construct_at` that worked within their system. Or their compile-time `tuple` used a completely different implementation from the non-compile-time one. My point is that there are many ways of implementing `tuple`, and some of them are incompatible with trivial copyability. – Nicol Bolas Jan 07 '23 at 19:18
6

Because std::tuple has copy/move ctor and assignment operators, it makes the class not-trivially-copyable.

See cpp reference:

A trivially copyable class is a class that

Has no non-trivial copy constructors (this also requires no virtual functions or virtual bases)
Has no non-trivial move constructors
Has no non-trivial copy assignment operators
Has no non-trivial move assignment operators
Has a trivial destructor

But std::tuple has all of the above constructors and assignment operators.

Mine
  • 4,123
  • 1
  • 25
  • 46