Both POD struct and std::tuple can be a great tools to wrap values in a single simple structure, and form the software design point of view there are only stylistical differences.

However I was wandering, from the implementation point of view, defining a POD struct is the same of defining a std::tuple.

For this purpose, a simple exercise is to compere the assembly code generated by the the following example [1]:

#include <tuple>

struct ThreeValues {
  char a;
  int b;
  double d;
};

void buildTuple(std::tuple<char, int, double>);
void buildStruct(ThreeValues);

void TupleExample() { buildTuple({'a', 1, 2.0}); }

void StructExample() { buildStruct({'a', 1, 2.0}); }

Compiling with gcc v13.1.0 using the -O3 optimization under the C++20 standard we obtain a lighter assembly code for the struct example.
The same result for the clang v18.1.0.

Under the hood

In order to understand this fact, we have to see the The Itanium C++ ABI (Application Binary Interface) specification.

The Itanium C++ ABI

The Itanium C++ ABI (Application Binary Interface) is a specification that defines how C++ code should be generated so that it can be linked together and executed, regardless of which compiler or platform is used. Although originally designed for the Itanium processor architecture, the ABI has been widely adopted beyond Itanium, especially by GCC and other compilers, because it provides a standardized way to handle complex C++ features [3].

Trivial and non-Trivial types

In the ABI specification we have the two following definition for trivial and non-trivial types [3]:

  • A type is considered trivial for the purposes of calls if it has simple copy semantics. Specifically, this means: -It has a trivial default constructor. -It has trivial copy and move constructors. -It has a trivial copy and move assignment operators. -It has a trivial destructor.
  • A type that does not meet the above criteria is considered non-trivial. This includes types with user-defined or complex copy/move constructors or destructors, or those with deleted copy/move constructors.

std::tuple as non-trivial type

std::tuple is a standard library type that can hold a fixed-size collection of heterogeneous values. Since it can contain other objects, its copy and move constructors, as well as its destructor, may need to perform more complex operations (like calling the copy/move constructors and destructors of the contained objects). This makes std::tuple non-trivial according to the ABI's criteria.

POD struct as trivial type

A POD struct in C++ typically satisfies the conditions for being a trivial type. Therefore, under the Itanium C++ ABI, a POD struct is indeed considered trivial for the purposes of calls. This means that a POD struct can be passed in registers if it meets the size and alignment constraints of the target architecture.

Final Comparison

Recalling the initial example, in order to generate a similar assembly code for the two structure we have to transform the POD struct in a non trivial types. This, it can be made adding a copy constructor to the ThreeValues struct [4]:

#include <tuple>

struct ThreeValues {

  ThreeValues(const char i_a, const int i_i, const double i_d)
      : a(i_a), i(i_i), d(i_d){};
  ThreeValues(const ThreeValues &i_threeValues)
      : a(i_threeValues.a), i(i_threeValues.i), d(i_threeValues.d) {}

  char a;
  int i;
  double d;
};

void buildTuple(std::tuple<char, int, double>);
void buildStruct(ThreeValues);

void TupleExample() { buildTuple({'a', 1, 2.0}); }

void StructExample() { buildStruct({'a', 1, 2.0}); }

References

[1]: C++ example 1 https://godbolt.org/z/nT1TTfKhz
[2]: Itanium C++ specification https://godbolt.org/z/1Yh4ohnqd
[3]: Cpp reference trivial class definition https://godbolt.org/z/1Yh4ohnqd
[4]: C++ example 2 https://godbolt.org/z/1Yh4ohnqd


Published

Category

Tips&Tricks

Tags

Contact