Skip to content
C++
Language
since C++11
Basic

User-Defined Literals

Define custom _suffix operators for strong types, units, and compile-time processing. Covers cooked vs raw forms, all operator signatures, and standard UDLs.

User-Defined Literalssince C++11

A mechanism allowing operator""_suffix functions to intercept numeric, character, and string literal tokens so that user-defined types can be constructed directly from literal syntax without any implicit conversion.

Overview

User-defined literals (UDLs) let you write 42_km, 3.14_rad, or "shutdown"_hash and have the compiler call a corresponding operator""_suffix function to produce a typed value. The result is fully typed β€” no silent conversions occur.

UDLs come in two processing forms:

Cooked β€” the compiler tokenizes and validates the literal normally, then passes the interpreted value to your operator. An integer literal becomes unsigned long long; floating-point becomes long double; string literals become const char* plus size_t. This is the common form and should be your default.

Raw β€” the compiler hands you the characters of the numeric token unchanged, either as a null-terminated const char* or as a variadic char template parameter pack. Raw forms apply only to integer and floating-point literals, not to string or character literals. Use raw forms when you need to inspect individual characters β€” for example, parsing binary or big-integer literals that overflow unsigned long long.

For a given numeric literal, the compiler selects operators in this priority order: cooked integer/float form first, then raw const char* form, then the template pack form last. If none of the applicable forms exist, the program is ill-formed.

Naming

All user-defined suffix names must begin with _. Suffixes without a leading underscore are reserved for the standard library β€” using them is undefined behavior. GCC and Clang both warn about this. Standard suffixes (s, sv, h, ms, i, etc.) live in nested inline namespace literals blocks, so callers can do using namespace std::chrono_literals without pulling in the entire std namespace.


Standard Library UDLs

cpp
// C++14 β€” std::string
#include <string>
using namespace std::string_literals;
auto s = "hello"s;              // std::string{"hello"}

// C++17 β€” std::string_view (no allocation)
#include <string_view>
using namespace std::string_view_literals;
auto sv = "hello"sv;            // std::string_view

// C++14 β€” std::chrono durations
#include <chrono>
using namespace std::chrono_literals;
auto t1 = 5h;                   // std::chrono::hours{5}
auto t2 = 30min;                // std::chrono::minutes{30}
auto t3 = 1500ms;               // std::chrono::milliseconds{1500}
auto t4 = 100us;                // std::chrono::microseconds{100}
auto t5 = 42ns;                 // std::chrono::nanoseconds{42}
auto t6 = 0.5s;                 // std::chrono::duration<double>{0.5}

// C++14 β€” std::complex
#include <complex>
using namespace std::complex_literals;
auto c = 1.0 + 2.0i;            // std::complex<double>{1.0, 2.0}

using namespace std::literals pulls in all of the above at once; the set of available UDLs grows with each standard revision (C++14 added string/chrono/complex; C++17 added string_view).


Syntax

cpp
// Integer literals (decimal, octal, hex, binary)
ReturnType operator""_suffix(unsigned long long);               // cooked β€” highest priority

// Floating-point literals
ReturnType operator""_suffix(long double);                      // cooked β€” highest priority

// String literals
ReturnType operator""_suffix(const char*, std::size_t);         // char (default)
ReturnType operator""_suffix(const wchar_t*, std::size_t);      // L"..."
ReturnType operator""_suffix(const char8_t*, std::size_t);      // u8"..." β€” C++20
ReturnType operator""_suffix(const char16_t*, std::size_t);     // u"..."
ReturnType operator""_suffix(const char32_t*, std::size_t);     // U"..."

// Character literals
ReturnType operator""_suffix(char);
ReturnType operator""_suffix(wchar_t);
ReturnType operator""_suffix(char8_t);                          // C++20
ReturnType operator""_suffix(char16_t);
ReturnType operator""_suffix(char32_t);

// Raw numeric forms β€” only for integer and floating-point tokens
ReturnType operator""_suffix(const char*);                      // raw string of token chars
template<char... Cs>
ReturnType operator""_suffix();                                 // template pack β€” lowest priority

When multiple overloads coexist for a numeric literal, cooked wins over raw const char*, which wins over the template form. Define only what you need.


Examples

Physical units with strong types

Providing both the long double and unsigned long long overloads lets callers write 3.5_m and 10_m without a cast.

cpp
#include <cmath>
#include <numbers>  // C++20

struct Meters { double value; };
struct Feet   { double value; };

constexpr Meters operator""_m(long double v)       { return {static_cast<double>(v)}; }
constexpr Meters operator""_m(unsigned long long v) { return {static_cast<double>(v)}; }
constexpr Feet   operator""_ft(long double v)      { return {static_cast<double>(v)}; }
constexpr Feet   operator""_ft(unsigned long long v){ return {static_cast<double>(v)}; }

constexpr Meters to_meters(Feet f) noexcept { return {f.value * 0.3048}; }

constexpr auto track  = 400.0_m;   // Meters{400.0}
constexpr auto hurdle = 3.0_ft;    // Feet{3.0}

// auto bad = track.value + hurdle.value;          // compiles but mixes units silently
auto ok = track.value + to_meters(hurdle).value;   // explicit, correct

Compile-time string hashing

String UDL operators receive a const char* and size_t. Marking the operator constexpr folds the hash to a constant, enabling switch dispatch on strings.

cpp
#include <cstdint>
#include <string_view>

constexpr std::uint32_t fnv1a32(const char* s, std::size_t len) noexcept {
    std::uint32_t h = 2166136261u;
    for (std::size_t i = 0; i < len; ++i)
        h = (h ^ static_cast<unsigned char>(s[i])) * 16777619u;
    return h;
}

constexpr std::uint32_t operator""_hash(const char* s, std::size_t len) noexcept {
    return fnv1a32(s, len);
}

void dispatch(std::string_view cmd) {
    switch (fnv1a32(cmd.data(), cmd.size())) {
        case "open"_hash:  handle_open();  break;
        case "close"_hash: handle_close(); break;
        default:           handle_unknown();
    }
}

Memory-size literals

cpp
#include <cstddef>

constexpr std::size_t operator""_KiB(unsigned long long n) { return n * 1024ULL; }
constexpr std::size_t operator""_MiB(unsigned long long n) { return n * 1024ULL * 1024; }
constexpr std::size_t operator""_GiB(unsigned long long n) { return n * 1024ULL * 1024 * 1024; }

std::vector<char> buf(4_MiB);          // 4,194,304 bytes
static_assert(2_GiB == 2147483648ULL);

Using IEC binary-prefix names (KiB/MiB/GiB) eliminates ambiguity with SI kilobytes.

Raw template form β€” compile-time validation

The template pack form receives each character of the numeric token as a non-type char parameter. This is the only way to validate or process a literal whose value overflows unsigned long long, or to enforce invariants at compile time.

cpp
#include <cstdint>

template<char... Cs>
constexpr std::uint8_t operator""_u8() {
    unsigned value = 0;
    for (char c : {Cs...}) {
        if (c == '\'') continue;    // skip C++14 digit separators
        value = value * 10 + static_cast<unsigned>(c - '0');
    }
    static_assert(value <= 255, "literal exceeds uint8_t range");
    return static_cast<std::uint8_t>(value);
}

constexpr std::uint8_t b = 200_u8;   // OK
// constexpr auto bad = 300_u8;      // static_assert fires at compile time

Note that C++14 digit separators (' inside a literal like 1'000) appear in the character pack and must be explicitly skipped.


Best Practices

Wrap UDLs in inline namespace literals. This mirrors the standard library pattern and lets consumers opt in with using namespace mylib::literals without importing the rest of your namespace.

cpp
namespace mylib {
    inline namespace literals {
        constexpr Meters operator""_m(long double v)   { return {(double)v}; }
        constexpr Feet   operator""_ft(long double v)  { return {(double)v}; }
    }
}

using namespace mylib::literals;  // only UDL operators enter scope

Mark operators constexpr by default. UDL operators are pure functions of their arguments. constexpr enables constant folding and allows use in static_assert, template arguments, and case labels.

Since C++20, consider consteval when the operator must only ever execute at compile time β€” for instance, a UDL that validates a format string or parses a structured value.

cpp
// C++20
consteval std::uint32_t operator""_ipv4(const char* s, std::size_t len) {
    return parse_and_validate_ipv4(s, len);  // compile error if s is not a valid IPv4
}

constexpr auto loopback = "127.0.0.1"_ipv4;  // uint32_t, verified at compile time

Provide both numeric overloads when the suffix makes semantic sense for integers and floats. 10_m and 10.5_m should both work without forcing the caller to write 10.0_m.


Common Pitfalls

Missing _ prefix. Any suffix that does not start with _ is reserved by the standard for future standard-library use. Defining operator""km is undefined behavior. Always write operator""_km. Both GCC (-Wliteral-suffix) and Clang warn about this.

Integer overflow in cooked operators. The compiler always passes unsigned long long, the widest available integer type. If the literal value exceeds ULLONG_MAX, the program is ill-formed. The only escape hatch is the raw template form, which receives raw characters and can implement arbitrary-precision parsing.

The const char* argument to string operators is null-terminated but len excludes the null. The standard guarantees that the pointer points to a null-terminated array of length len + 1, but use the len parameter for all length-sensitive operations β€” do not scan for '\0' to find the end, since embedded nulls are possible in string literals.

using namespace std::literals in headers. This pollutes every translation unit that includes the header. Confine using namespace directives to .cpp files or function bodies, or document explicitly when namespace-scope inclusion is intentional.

Ambiguity with suffixes already used as identifiers. If your codebase has a variable named _m or a macro ending in _m, UDL suffixes can create surprising parse conflicts. Prefer longer, domain-specific names like _meters or _mib to minimize collision risk.


See Also

  • string-literals β€” raw string literals (R"(...)") and encoding prefixes (u8, u, U, L)
  • if-constexpr β€” compile-time branching usable inside constexpr UDL operators
  • structured-bindings β€” another syntax-level facility for integrating user-defined types with language idioms