skip to content
Mehdi Mehdikhani
Table of Contents

User-defined literals in C++11 let you create custom suffixes for values, making code more readable and type-safe. They’re not as common as other features, but they’re quite useful when you need them.

What User-Defined Literals Actually Do

They let you define custom suffixes that convert raw values into specific types:

// Define a literal for creating std::string
std::string operator""_s(const char* str, size_t len) {
return std::string(str, len);
}
auto text = "Hello World"_s; // Creates std::string, not const char*

The compiler transforms "Hello World"_s into a call to your operator function.

Standard Library Examples

The standard library provides several useful literals:

#include <chrono>
#include <string>
using namespace std::chrono_literals;
using namespace std::string_literals;
auto duration = 500ms; // std::chrono::milliseconds
auto timeout = 30s; // std::chrono::seconds
auto text = "Hello"s; // std::string
auto raw = "Hello"; // const char*
// Complex numbers (C++14)
#include <complex>
using namespace std::complex_literals;
auto z = 3.0 + 4.0i; // std::complex<double>

When I Actually Use User-Defined Literals

Most of the time, I create literals for:

  1. Unit conversions: Making physical units explicit:
constexpr double operator""_km(long double km) {
return km * 1000.0; // Convert to meters
}
constexpr double operator""_mph(long double mph) {
return mph * 0.44704; // Convert to m/s
}
double distance = 5.5_km; // 5500.0 meters
double speed = 60_mph; // 26.8224 m/s
  1. Configuration values: Type-safe configuration:
struct Size {
size_t bytes;
constexpr Size(size_t b) : bytes(b) {}
};
constexpr Size operator""_KB(unsigned long long kb) {
return Size(kb * 1024);
}
constexpr Size operator""_MB(unsigned long long mb) {
return Size(mb * 1024 * 1024);
}
Size cache_size = 64_MB;
Size buffer_size = 8_KB;
  1. Binary and hex literals: Making bit patterns clearer:
constexpr uint32_t operator""_binary(const char* str) {
uint32_t result = 0;
for (const char* p = str; *p; ++p) {
if (*p == '1') {
result = (result << 1) | 1;
} else if (*p == '0') {
result = result << 1;
}
// Ignore spaces and underscores for readability
}
return result;
}
uint32_t mask = "1010_0011_1100_0001"_binary; // More readable than 0xa3c1
  1. JSON-like syntax: Domain-specific languages:
#include <json> // Hypothetical JSON library
json::Value operator""_json(const char* str, size_t) {
return json::parse(str);
}
auto config = R"({
"server": {
"port": 8080,
"host": "localhost"
}
})"_json;

Different Literal Types

You can define literals for different value types:

// Integer literal
constexpr std::chrono::seconds operator""_sec(unsigned long long s) {
return std::chrono::seconds(s);
}
// Floating-point literal
constexpr std::chrono::duration<double> operator""_sec(long double s) {
return std::chrono::duration<double>(s);
}
// String literal
Color operator""_color(const char* str, size_t len) {
return Color::from_hex_string(std::string(str, len));
}
// Character literal (rare)
constexpr int operator""_ascii(char c) {
return static_cast<int>(c);
}
auto timeout1 = 30_sec; // From integer
auto timeout2 = 2.5_sec; // From floating-point
auto bg_color = "#FF0000"_color; // From string

Real World Example

Here’s how I use them in a graphics library:

struct Color {
uint8_t r, g, b, a;
static Color from_hex(uint32_t hex) {
return {
static_cast<uint8_t>((hex >> 24) & 0xFF),
static_cast<uint8_t>((hex >> 16) & 0xFF),
static_cast<uint8_t>((hex >> 8) & 0xFF),
static_cast<uint8_t>(hex & 0xFF)
};
}
};
constexpr Color operator""_rgb(unsigned long long hex) {
return Color::from_hex(static_cast<uint32_t>(hex));
}
// Usage is much cleaner
Color red = 0xFF0000FF_rgb;
Color blue = 0x0000FFFF_rgb;
Color transparent = 0x00000000_rgb;

The Naming Rules

Literal operators must start with underscore or be in the std namespace:

// OK - starts with underscore
std::string operator""_s(const char*, size_t);
// Error - reserved for standard library
std::string operator""s(const char*, size_t);
// OK - but don't do this unless you're implementing standard library
namespace std {
string operator""s(const char*, size_t);
}

Template Literals (C++14)

C++14 added template literals for character sequences:

template<char... chars>
constexpr auto operator""_hash() {
return compute_hash<chars...>();
}
auto hash = "hello"_hash; // Template instantiated with 'h','e','l','l','o'

These are more complex but can be very powerful.

The Pattern I Follow

I use user-defined literals when they make code significantly more readable:

// Time durations
auto short_timeout = 100ms;
auto long_timeout = 30s;
// Sizes and units
auto packet_size = 1_KB;
auto disk_space = 500_GB;
// Colors and graphics
auto background = "#F0F0F0"_color;
auto border_width = 2_px;

But I don’t go overboard - too many custom literals can make code confusing. The goal is clarity, not cleverness.

User-defined literals are a nice way to make code more expressive and type-safe. They’re especially useful for domain-specific types and units.