Which type trait would indicate that type is memcpy assignable? (tuple, pair)
This is only a partial answer to your question:
Type traits don't necessarily mean what their name says literally.
Specifically, let's take std::is_trivially_copyable
. You were - rightly - surprised that a tuple of two double's is not trivially copyable. How could that be?!
Well, the trait definition says:
If
T
is aTriviallyCopyable
type, provides the member constantvalue
equaltrue
. For any other type,value
isfalse
.
and the TriviallyCopyable
concept has the following requirement in its definition:
- Every copy constructor is trivial or deleted
- Every move constructor is trivial or deleted
- Every copy assignment operator is trivial or deleted
- Every move assignment operator is trivial or deleted
- At least one copy constructor, move constructor, copy assignment operator, or move assignment operator is non-deleted
- Trivial non-deleted destructor
Not quite what you would expect, right?
With all in mind, it's not necessarily the case that any of the standard library traits would combine to fit the exact requirements of "constructible by memcpy()
'ing".
The correct test is in fact std::is_trivially_copyable
, which allows use of memcpy
for both making a new object and modifying an existing one.
Although you may be surprised that these return false for types where your intuition tells you that memcpy
ought to be ok, they are not lying; the Standard indeed makes memcpy
undefined behavior in these cases.
In the particular case of std::pair
, we can get some insight into what goes wrong:
int main()
{
typedef std::pair<double,double> P;
std::cout << "\nTC: " << std::is_trivially_copyable<P>::value;
std::cout << "\nTCC: " << std::is_trivially_copy_constructible<P>::value;
std::cout << "\nTCv: " << std::is_trivially_constructible<P, const P&>::value;
std::cout << "\n CC: " << std::is_copy_constructible<P>::value;
std::cout << "\n MC: " << std::is_move_constructible<P>::value;
std::cout << "\nTCA: " << std::is_trivially_copy_assignable<P>::value;
std::cout << "\nTCvA:" << std::is_trivially_assignable<P, const P&>::value;
std::cout << "\n CA: " << std::is_copy_assignable<P>::value;
std::cout << "\n MA: " << std::is_move_assignable<P>::value;
std::cout << "\nTD: " << std::is_trivially_destructible<P>::value;
}
TC: 0 TCC: 1 TCv: 1 CC: 1 MC: 1 TCA: 0 TCvA:0 CA: 1 MA: 1 TD: 1
Evidently it isn't trivially copy assignable.1
The pair
assignment operator is user-defined, so not trivial.
1I think that clang, gcc, and msvc are all wrong here, actually, but if it satisfied std::_is_trivially_copy_assignable
it wouldn't help, because TriviallyCopyable requires that the copy constructor, if not deleted, is trivial, and not the TriviallyCopyAssignable trait. Yeah, they're different.
A copy/move assignment operator for class X is trivial if it is not user-provided and...
vs
is_assignable_v<T, const T&>
is true and the assignment, as defined byis_assignable
, is known to call no operation that is not trivial.
The operations called by pair<double, double>
's copy assignment operator are the assignments of individual doubles, which are trivial.
Unfortunately, the definition of trivially copyable relies on the first, which pair
fails.
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.