Reputation: 7673
My question is the following:
If I want to copy a class type, memcpy can do it very fast. This is allowed in some situations.
We have some type traits:
What I would like to know is the exact requirements when a type will be "bitwise copyable".
My conclusion is that a type is bitwise copyable if both of is_trivally_copyable
and is_standard_layout
traits are true:
P.S.: of course, the result of memcpy must be correct. I know I could memcpy in any situation but incorrectly.
Upvotes: 28
Views: 5292
Reputation: 361692
If you have serialized bytes (like std::vector<uint8_t>
that you read from a file or received over a network) and want to efficiently deserialize these bytes into a C++ type (assuming the type has been properly designed to take care of the padding and order of members), then you can do the following:
Account account{}; // assuming it has taken care of paddings, size, order of members and other relevant stuff.
std::memcpy(&account, bytes, sizeof(Account)); // super efficient deserialization
// use account here.
But wait. Not that fast.
You cannot simply use memcpy
with any types, but only with is_trivially_copyable
types but then again, is_trivially_copyable
types do NOT guarantee any specific/predictable memory-layout because the compiler may reorder the members in any way it wants. On the other hand, is_standard_layout
types do have predictable memory layout but the problem is, no where in the Standard it says, you can memory bytes into is_standard_layout
types.
That means, you want a is_standard_layout
which is also is_trivially_copyable
.
A standard_layout
type can be is_trivially_copyable
if the type does NOT have non-trivial (user-provided) destructor.
That means, you can define a concept is_memcpyable_standard_layout
as:
template<typename T>
concept is_memcpyable_standard_layout = std::is_standard_layout_v<T>
and std::is_trivially_copyable_v<T>
and std::is_trivially_destructible_v<T>;
I feel the last part is "probably" not necessary but I used that for additional safety in case I missed anything important out, or conversely, you can remove the middle part and it should still work, but I kept that as well, for the same reason.
Now you can write code like:
template<typename T>
auto deserialize_into(T & out, std::vector<uint8_t> const & bytes) -> void {
if constexpr (is_memcpyable_standard_layout<T>) {
assert(sizeof(T) == bytes.data());
std::memcpy(out, bytes.data(), sizeof (T));
} else {
deserialize_manually_into(out, bytes); // manual deserialization
}
}
Hope that helps.
Upvotes: 0
Reputation: 39119
If is_trivally_copyable<T>::value
(or in C++14 is_trivially_copyable<T>()
, or in C++17 is_trivially_copyable_v<T>
) is not zero, the type is copyable using memcpy
.
Per the C++ standard, a type being trivially copyable means:
the underlying bytes making up the object can be copied into an array of char or unsigned char. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value.
However, it is important to realise that pointers are trivially copyable types, too. Whenever there are pointers inside the data structures you will be copying, you have to brainually make sure that copying them around is proper.
Examples where hazard may be caused by just relying on the object being trivially copyable:
So whenever memcopying, keep in mind to check whether pointers could be copied in that specific case, and if that would be okay.
Realise that is_trivially_copyable
is only the "Syntax Check", not the "Semantic Test", in compiler parlance.
Upvotes: 15
Reputation: 88215
You can copy an object of type T using memcpy
when is_trivially_copyable<T>::value
is true. There is no particular need for the type to be a standard layout type. The definition of 'trivially copyable' is essentially that it's safe to do this.
An example of a class that is safe to copy with memcpy
but which is not standard layout:
struct T {
int i;
private:
int j;
};
Because this class uses different access control for different non-static data members it is not standard layout, but it is still trivially copyable.
Upvotes: 26
Reputation: 31577
Objects with trivial copy constructors, trivial copy assignment operators and
trivial destructors can be copied with memcpy
or memmove
The requirements for a special member function of a class T
to be trivial are
T
has no virtual member functionsT
has no virtual base classesT
is trivialT
is trivial T
has no non-static data members of volatile-qualified type (since C++14)Just declaring the function as = default
doesn’t make it trivial (it will only be trivial if
the class also supports all the other criteria for the corresponding function to be trivial)
but explicitly writing the function in user code does prevent it from being trivial. Also all data types compatible with the C language (POD types) are trivially copyable.
Source : C++ Concurrency in action and cppreference.com
Upvotes: 7
Reputation: 1458
What I understood is
you can test if given type is pod (Plain Old Data) by using standard function is_pod::value
Reference: The C++ programming Language 4th edition
Upvotes: 2
Reputation: 206717
From http://en.cppreference.com/w/cpp/types/is_trivially_copyable:
Objects of trivially-copyable types are the only C++ objects that may be safely copied with
std::memcpy
or serialized to/from binary files withstd::ofstream::write()/std::ifstream::read()
. In general, a trivially copyable type is any type for which the underlying bytes can be copied to an array of char or unsigned char and into a new object of the same type, and the resulting object would have the same value as the original.
Upvotes: 7