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?
std::pair has a non-trivial copy-assignment and move-assignment operator. This prevents it from being trivially copyable. Since C++17, if one of the two contained types is not assignable, then the copy/move assignment operator is defined as deleted, which lifts this restriction on being trivially copyable.
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.
The trivially copied std::function has some dependency on original object memory, delete the original memory will trash the badly copied std::function even without the destruction of the original object.
std::array however, has a static size set at compile time. It does not have internal pointers and can therefore be copied simply by using memcpy . It therefore is trivial to copy.
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.
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.
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