Trivial and Standard-Layout Types in C++ Explained

In C++, low-level memory operations and inter-language compatibility rely on specific guarantees about object construction, destruction, and memory arrangement. This article provides a direct overview of trivial types, which can be safely copied using raw byte operations without custom lifecycle logic, and standard-layout types, which guarantee a predictable memory organization compatible with the C programming language. Understanding these two concepts clarifies how modern C++ categorizes data structures and replaces the legacy concept of Plain Old Data (POD).

What Is a Trivial Type?

A trivial type in C++ is a type whose fundamental lifecycle operations—such as creation, copying, moving, and destruction—involve no custom user logic and can be handled via direct memory operations like std::memcpy.

A class, struct, or union is considered trivial if it meets the following criteria:

  • Trivial default constructor: It has either an implicitly generated default constructor or one explicitly defined with = default.
  • Trivial copy and move operations: Its copy constructors, move constructors, copy assignment operators, and move assignment operators are all trivial or deleted.
  • Trivial destructor: Its destructor is not user-defined and is not virtual.
  • Trivial members and bases: All base classes and non-static data members must also be trivial types.
  • No virtual features: It contains no virtual functions and inherits from no virtual base classes.

Scalar types (such as int, double, pointers, and enumerations) and arrays of trivial types are inherently trivial.

The primary benefit of a trivial type is performance. The compiler can bypass constructor and destructor function calls entirely, allocating and freeing memory instantaneously. You can query whether a type satisfies this property at compile time using the type trait std::is_trivial<T>::value (or std::is_trivially_copyable<T>::value).

What Is a Standard-Layout Type?

A standard-layout type governs the physical arrangement of an object's members in memory. It guarantees that the memory alignment and member offsets are completely predictable, making the type binary-compatible with the C language.

Unlike triviality, standard-layout has nothing to do with whether constructors or destructors contain custom code. Instead, it enforces strict structural rules:

  • Uniform access control: All non-static data members must have the same access specifier (public, protected, or private). Mixing access levels is not allowed because compilers are permitted to order members with different access levels arbitrarily.
  • No virtual features: It has no virtual functions and no virtual base classes.
  • Member hierarchy rules: Either the most-derived class has non-static data members and no base class with non-static data members, or all non-static data members are located in a single base class.
  • No identical base types as first member: The first non-static data member cannot be of the same type as any base class, preventing layout conflicts under the Empty Base Optimization (EBO).
  • Standard-layout members and bases: All non-static data members and base classes must themselves be standard-layout types.

Standard-layout types allow developers to use the offsetof macro safely to inspect member offsets and guarantee safe data exchange with foreign function interfaces (FFIs), such as C or hardware registers. This property can be checked using std::is_standard_layout<T>::value.

Key Differences and Modern POD

While triviality and standard-layout often overlap, they address two orthogonal concerns:

Feature Trivial Type Standard-Layout Type
Primary Focus Object lifecycle (construction, copying, destruction) Physical memory arrangement
Virtual Functions Allowed? No No
Custom Constructors Allowed? No Yes
Mixed Access Specifiers Allowed? Yes No
Safe for std::memcpy? Yes Only if trivially copyable
Safe for C Compatibility? Not guaranteed Yes

Historically, C++ defined a "Plain Old Data" (POD) type as a type that was both trivial and standard-layout. However, C++20 formally deprecated std::is_pod because bundling both concepts into a single term caused unnecessary confusion. Modern C++ encourages developers to query std::is_trivially_copyable when optimizing copies or serialization, and std::is_standard_layout when ensuring ABI and memory-layout compatibility.