-
Notifications
You must be signed in to change notification settings - Fork 4
Description
Contact: Mingxin Wang
The current definition of "trivially relocatable" in P2786 R13 does not guarantee bitwise relocatability (i.e., that
an object can be relocated via memcpy
). This significantly weakens the utility of the feature, especially for
performance-critical applications such as type erasure, and creates a gap that other proposals (e.g., P3780R0
is_bitwise_trivially_relocatable
) are trying to fill. We believe that "trivially relocatable" is the correct and
intuitive place for this guarantee.
Proposed change:
Modify 11.2 [class.prop] to reflect the bitwise copy requirement and implementation-defined behavior for
special pointers.*
Change the definition of when a class is eligible for trivial relocation as follows:
A class is *eligible for trivial relocation* unless it
— has any virtual base classes,
— has a base class that is not a trivially relocatable class,
— has a non-static data member of an object type that is not of a trivially relocatable type, or
+ — has a subobject of a type where a bitwise copy may not preserve the semantics of the object (e.g., types
with pointer authentication), or
— has a deleted destructor,
except that it is implementation-defined whether an otherwise-eligible union having one or more subobjects of
polymorphic class type is eligible for trivial relocation.
Detailed Comments
The "Trivial" in "Trivially Relocatable" Should Imply Bitwise Operation
The term "trivial" in C++ has a strong precedent for implying bitwise operations (e.g., trivially copyable). When a
developer sees "trivially relocatable", the natural and overwhelming expectation is that the object can be moved
with memcpy
. Deviating from this expectation makes the feature less intuitive and harder to use correctly.
The argument that certain architectures or compiler optimizations (like pointer authentication) might require
more than a memcpy
is noted. However, we believe this concern is misplaced for a "trivial" trait. If a type
requires special handling for relocation, it should not be considered "trivially relocatable", in the same way that
a polymorphic type with a vptr is not trivially copyable. The complexity should disqualify the type from being
"trivial," rather than watering down the meaning of "trivial" for all users.
Critical Need for Bitwise Relocatability in Type Erasure
Type erasure libraries are a primary motivator for this feature. These libraries manage objects of unknown
types through a common interface. For performance, they need to be able to relocate their owned objects in
memory (e.g., when a std::vector
-like buffer reallocates) using memcpy
. Without a guarantee of bitwise
relocatability, these libraries face a dilemma:
- Assume
memcpy
is safe: This is what many libraries do today for types that are
std::is_trivially_copyable_v && std::is_trivially_destructible_v
, but it is not guaranteed to be correct for all
relocatable types under the proposed definition. This leads to subtle, hard-to-debug issues on platforms where
the assumption fails. - Use the move constructor: This is safer but incurs a significant performance penalty, defeating one of the
main purposes of introducing a relocation mechanism.
Since type erasure facilities operate without compile-time type information, they cannot check for specific types
that might require special relocation logic. They need a single, reliable query (is_trivially_relocatable
) that
guaranteesmemcpy
is safe.
Proposed Solution
We propose that the definition of "trivially relocatable" be strengthened to guarantee bitwise relocatability. This
ensures that std::is_trivially_relocatable_v<T>
provides the strong, portable guarantee that developers need:
if it is true
, memcpy
is safe.
To address the concerns about special pointer types (e.g., with pointer authentication), we suggest the
following:
Make it implementation-defined whether types with special pointer semantics (like those requiring pointer
authentication) are considered is_trivially_relocatable
.
This approach has precedent. The behavior of many aspects of the C++ memory model and type system is
implementation-defined, allowing vendors to make choices appropriate for their platforms. An implementation
where pointers require special handling upon relocation would simply not mark types containing them as
trivially relocatable. This puts the burden of complexity on the implementation, where it belongs, rather than on
the user.
This makes the feature significantly more valuable and less error-prone for the entire C++ community.