std::is_standard_layout and C Memory Compatibility
In modern C++, intersystem communication and low-level
interoperability often depend on whether an object can safely cross the
language boundary into C. The compile-time trait
std::is_standard_layout, defined in the
<type_traits> header, provides a definitive check for
this capability. This article explores how
std::is_standard_layout evaluates class architecture, the
specific layout constraints it enforces, and why meeting these rules
guarantees memory-level compatibility with standard C code.
The Purpose of Standard-Layout Types
C++ expands upon C structures by introducing inheritance, access specifiers, and polymorphism. However, these features can cause compilers to alter the physical memory layout of an object—such as injecting hidden pointers or rearranging members in memory.
A "standard-layout" type is a C++ type whose data arrangement is
predictable, uniform across conforming compilers, and identical to how a
conventional C compiler would lay out a struct or
union. The compile-time boolean
std::is_standard_layout<T>::value evaluates whether
type T avoids C++-specific memory mechanisms, guaranteeing
binary layout compatibility with C APIs.
Rules Evaluated by
std::is_standard_layout
To evaluate to true, a class, struct, or union must
satisfy several strict structural requirements set by the C++
standard:
No Virtual Functions or Virtual Base Classes
Virtual functions and virtual inheritance require runtime polymorphism, typically implemented via a virtual method table pointer (vptr). Compilers may place thevptrat the beginning or elsewhere within the object payload, breaking the flat, predictable memory alignment expected by C.Uniform Access Control for Non-Static Data Members
All non-static data members must share the same access control level (public,protected, orprivate). When access levels differ, the C++ specification permits the compiler to reorder members in memory (for instance, grouping allpublicmembers together followed byprivatemembers). Uniform access preserves lexical declaration order in memory.Restricted Class Hierarchies
Inheritance is permitted, but non-static data members may only appear in one class within the hierarchy. Either the derived class has non-static members and all base classes are empty, or the base class contains the members and the derived class introduces only member functions. This prevents compiler-specific padding and member-interleaving across class boundaries.Empty Base Class Optimization Constraints
The first non-static data member of a derived class cannot be of the same type as any of its base classes. This prevents address collisions that occur when applying the Empty Base Optimization (EBO), ensuring every distinct subobject has a distinct memory address.Recursive Conformance of Members and Bases
All non-static data members, arrays of members, and base classes must themselves be standard-layout types. Fundamental types (such asint,double, and raw pointers) are inherently standard-layout.
How Standard-Layout Guarantees C Compatibility
When std::is_standard_layout<T>::value yields
true, it guarantees several critical low-level memory
characteristics required by the C ABI:
- Predictable Member Offsets: The compiler orders
non-static data members in memory according to their declaration order,
adhering to normal platform alignment rules just like a C
struct. You can reliably query member offsets using theoffsetofmacro. - Pointer Interchangeability: A pointer to a
standard-layout class object can be safely converted via
reinterpret_castto a pointer to its initial non-static data member (and vice versa). The address of the object is strictly identical to the address of its first member, matching C behavior. - Common Initial Sequence: Two standard-layout union members (or structs) that share an initial sequence of types can have that common sequence inspected portably, mirroring the union behavior defined in the C language standard.
- Safe Byte Transfers: Standard-layout objects with
trivial copy operations can be serialized, copied with
std::memcpy, or passed across C interfaces without unintended structural side effects.
By evaluating these invariants at compile time,
std::is_standard_layout allows developers to write static
assertions that prevent architectural changes from silently breaking
low-level C ABI compatibility.