The power of c++11 user-defined literals

29 April 2023

C++11 introduced a powerful feature called user-defined literals (UDLs) that allows us to create custom literals with their own meaning and syntax. This addition enables more expressive and readable code, especially when dealing with specific domains, such as units of measurement, complex numbers, or even binary literals.

Contents

What are UDLs?

Literals are constant values used directly in the code, such as integers (e.g., 42), floating-point numbers (e.g., 3.14), characters (e.g., 'A'), or strings (e.g., "Hello world!).

User-defined literals allow us to create new literals with custom syntax and semantics. These literals are formed by combining a constant value with custom suffix, such as 42_km or 3.14_rad.

Syntax

To define and UDL, you need to create a function with the following signature:

<T> operator "" _<suffix>(Args...);

Where <T> is the return type, <suffix> is the user-defined suffix, and Args... are the arguments passed to the function.

Also note the use of underscore, this naming convention is designed -and necessary- to avoid potential conflicts with future standard library additions (in contrast with identifiers, where underscore followed by a letter in the global scope is reserved for the implementation).

Examples

A common case for UDLs is to represent units of measurement in a more expressive and readable way. For instance, intead of writing:

double distance = 1000.0; // meters

You can define a UDL to represent meters:

#include <iostream>

struct Meter {
  double value;
};

Meter operator"" _m(double value) {
  return Meter{value};
}

int main() {
  Meter distance = 1000.0_m;
  std::cout << "Distance: " << distance.value << " meters" << std::endl;
  return 0;
}

Another example is creating complex numbers using UDLs:

#include <iostream>
#include <complex>

std::complex<double> operator"" _i(double value) {
  return std::complex<double>(0, value);
}

int main() {
  auto complex_number = 3.0 + 4.0_i;
  std::cout << "Complex number: " << complex_number << std::endl;
  return 0;
}

We define the _i suffix to represent the imaginary part of a complex number. This makes the code more expressive because it resembles the mathematical notation of complex numbers.

UDLs can also be used to create binary literals, and I know, they exists (e.g., 0b0010), but it's interesting to make our own:

#include <iostream>

constexpr unsigned int operator"" _b(const char* str, std::size_t) {
  unsigned int value = 0;
  for (const char* ptr = str; *ptr; ++ptr) {
      value = (value << 1) | (*ptr - '0');
  }
  return value;
}

int main() {
  unsigned int value = 1101_b;
  std::cout << "Decimal value: " << value << std::endl;
  return 0;
}

Note the std::size_t parameter, when the UDL function receives an string literal it has a second parameter, the std::size_t, it represents the length of the string literal, but since we are not using it we can leave it there without a name.