How std::complex Works in C++
This article provides an overview of the std::complex
class template defined in the C++ <complex> header,
explaining how the C++ Standard Library models mathematical complex
numbers. It details the internal memory layout, template
specializations, interoperability guarantees with C arrays and C99
complex numbers, and the core interfaces used for initialization,
component access, and arithmetic manipulation.
Type Definition and Specializations
The C++ standard represents complex numbers—values of the form \(a + bi\), where \(a\) is the real part, \(b\) is the imaginary part, and \(i^2 = -1\)—using the class template
std::complex<T>. The ISO C++ standard specifies
explicit specializations for the three fundamental floating-point
types:
std::complex<float>std::complex<double>std::complex<long double>
While the primary template can technically be instantiated with custom types satisfying numeric requirements, standard compliance and full algorithmic support (such as transcendentals) are guaranteed primarily for these three floating-point types.
Memory Layout and Interoperability
One of the defining design requirements of std::complex
is its binary representation. The standard guarantees that for an object
of type std::complex<T>, the real and imaginary parts
are stored sequentially in memory:
- The real component is stored first at index
0. - The imaginary component is stored immediately adjacent at index
1.
Because of this requirement:
- A
std::complex<T>*can be safely cast or reinterpreted as an array of two elementsT[2]. - An array of
std::complex<T>is laid out consecutively as alternating real and imaginary components (real0, imag0, real1, imag1, ...). - The layout is strictly binary-compatible with the C99
float _Complex,double _Complex, andlong double _Complextypes, as well as the C structure layout containing two consecutive elements of typeT.
This makes std::complex zero-overhead and directly
compatible with high-performance linear algebra libraries (such as BLAS
or LAPACK) without requiring data transformation.
Construction and Component Access
Instances of std::complex can be initialized via
constructors or user-defined literals introduced in C++14:
#include <complex>
// Constructor initialization
std::complex<double> z1(3.0, 4.0); // 3 + 4i
// Standard literal initialization (requires namespace std::complex_literals)
using namespace std::complex_literals;
auto z2 = 3.0 + 4.0i;Component access is provided through member and non-member functions:
.real()returns or sets the real part..imag()returns or sets the imaginary part.- In C++20 and later,
real()andimag()are also markedconstexpr.
Mathematical Operations
The <complex> library provides complete operator
overloads for standard arithmetic:
- Addition (
+), subtraction (-), multiplication (*), and division (/) work for complex-complex pairs and mixed complex-scalar operations. - Relational equality operators (
==,!=) compare both components for equality.
Additionally, standard mathematical functions are overloaded for
std::complex:
- Magnitude and Angle:
std::abs(z)computes the magnitude (\(\sqrt{a^2 + b^2}\)),std::norm(z)computes the squared magnitude (\(a^2 + b^2\)), andstd::arg(z)computes the phase angle. - Polar Representation:
std::polar(rho, theta)constructs a complex number from magnitude and angle. - Conjugation:
std::conj(z)yields \(a - bi\). - Transcendental Functions: Exponential, logarithmic,
power, trigonometric, and hyperbolic functions (e.g.,
std::exp,std::log,std::sin,std::sqrt) are implemented handling branch cuts defined by the ISO standard.