User-Defined Literals
/ 4 min read
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::stringstd::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::millisecondsauto timeout = 30s; // std::chrono::secondsauto text = "Hello"s; // std::stringauto 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:
- 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 metersdouble speed = 60_mph; // 26.8224 m/s- 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;- 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- 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 literalconstexpr std::chrono::seconds operator""_sec(unsigned long long s) { return std::chrono::seconds(s);}
// Floating-point literalconstexpr std::chrono::duration<double> operator""_sec(long double s) { return std::chrono::duration<double>(s);}
// String literalColor 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 integerauto timeout2 = 2.5_sec; // From floating-pointauto bg_color = "#FF0000"_color; // From stringReal 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 cleanerColor 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 underscorestd::string operator""_s(const char*, size_t);
// Error - reserved for standard librarystd::string operator""s(const char*, size_t);
// OK - but don't do this unless you're implementing standard librarynamespace 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 durationsauto short_timeout = 100ms;auto long_timeout = 30s;
// Sizes and unitsauto packet_size = 1_KB;auto disk_space = 500_GB;
// Colors and graphicsauto 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.