I have found the intricacies of trivial types in C++ non-trivial to understand and hope someone can enlighten me on the following.
Given type T
, storage for T
allocated using ::operator new(std::size_t)
or ::operator new[](std::size_t)
or std::aligned_storage
, and a void * p
pointing to a location in that storage suitably aligned for T
so that it may be constructed at p
:
std::is_trivially_default_constructible<T>::value
holds, is the code invoking undefined behavior when code skips initialization of T
at p
(i.e. by using T * tPtr = new (p) T();
) before otherwise accessing *p
as T
? Can one just use T * tPtr = static_cast<T *>(p);
instead without fear of undefined behavior in this case?std::is_trivially_destructible<T>::value
holds, does skipping destruction of T
at *p
(i.e by calling tPtr->~T();
) cause undefined behavior?U
for which std::is_trivially_assignable<T, U>::value
holds, is std::memcpy(&t, &u, sizeof(U));
equivalent to t = std::forward<U>(u);
(for any t
of type T
and u
of type U
) or will it cause undefined behavior?According to the Thomas-Kilmann Conflict Mode Instrument (TKI), used by human resource (HR) professionals around the world, there are five major styles of conflict management—collaborating, competing, avoiding, accommodating, and compromising.
A trivial type is a type whose storage is contiguous (trivially copyable) and which only supports static default initialization (trivially default constructible), either cv-qualified or not. It includes scalar types, trivial classes and arrays of any such types.
Again, collaborating is normally the best strategy for handling conflicts over important issues. When dealing with moderately important issues, compromising can often lead to quick solutions.
No, you can't. There is no object of type T
in that storage, and accessing the storage as if there was is undefined. See also T.C.'s answer here.
Just to clarify on the wording in [basic.life]/1, which says that objects with vacuous initialization are alive from the storage allocation onward: that wording obviously refers to an object's initialization. There is no object whose initialization is vacuous when allocating raw storage with operator new
or malloc
, hence we cannot consider "it" alive, because "it" does not exist. In fact, only objects created by a definition with vacuous initialization can be accessed after storage has been allocated but before the vacuous initialization occurs (i.e. their definition is encountered).
Omitting destructor calls never per se leads to undefined behavior. However, it's pointless to attempt any optimizations in this area in e.g. templates, since a trivial destructor is just optimized away.
Right now, the requirement is being trivially copyable, and the types have to match. However, this may be too strict. Dos Reis's N3751 at least proposes distinct types to work as well, and I could imagine this rule being extended to trivial copy assignment across one type in the future.
However, what you've specifically shown does not make a lot of sense (not least because you're asking for assignment to a scalar xvalue, which is ill-formed), since trivial assignment can hold between types whose assignment is not actually "trivial", that is, has the same semantics as memcpy
. E.g. is_trivially_assignable<int&, double>
does not imply that one can be "assigned" to the other by copying the object representation.
A trivial default constructor is a constructor that performs no action. All data types compatible with the C language (POD types) are trivially default-constructible. Unlike in C, however, objects with trivial default constructors cannot be created by simply reinterpreting suitably aligned storage, such as memory allocated with std::malloc: placement-new is required to formally introduce a new object and avoid potential undefined behavior.
But the note says it's a formal limitation, so probably it is safe in many cases. Not guaranteed though.
is_assignable
does not even guarantee the assignment will be legal under certain conditions:This trait does not check anything outside the immediate context of the assignment expression: if the use of T or U would trigger template specializations, generation of implicitly-defined special member functions etc, and those have errors, the actual assignment may not compile even if std::is_assignable::value compiles and evaluates to true.
What you describe looks more like is_trivially_copyable, which says:
Objects of trivially-copyable types are the only C++ objects that may be safely copied with std::memcpy or serialized to/from binary files with std::ofstream::write()/std::ifstream::read().
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