C++ User-Defined Literals Complete Guide
이 글의 핵심
Master C++ user-defined literals: custom suffix operators for expressive, type-safe code. Complete guide with integer, float, string, raw literals, and production patterns.
What are User-Defined Literals?
User-defined literals let you create custom suffixes for literals, making code more expressive and type-safe.
// Standard literals
// 변수 선언 및 초기화
int x = 10;
double y = 3.14;
string s = "hello";
// User-defined literals
Distance d = 10_km; // Custom suffix
Angle a = 3.14_deg; // Custom suffix
auto str = "hello"_s; // std::string
Benefits:
- Type safety
- Expressive syntax
- Compile-time evaluation
- Domain-specific abstractions
Basic Syntax
User-defined literals are implemented as operator"" functions:
// Integer literal
constexpr long long operator"" _km(unsigned long long value) {
return value * 1000; // Convert km to meters
}
int main() {
auto distance = 10_km; // 10000 meters
cout << distance << endl; // 10000
}
Key: The operator"" function is called with the literal value, returning the desired type.
Literal Types
1. Integer Literals
constexpr unsigned long long operator"" _KB(unsigned long long value) {
return value * 1024;
}
constexpr unsigned long long operator"" _MB(unsigned long long value) {
return value * 1024 * 1024;
}
int main() {
auto size1 = 10_KB; // 10240
auto size2 = 5_MB; // 5242880
cout << size1 << ", " << size2 << endl;
}
Output:
10240, 5242880
2. Floating-Point Literals
constexpr long double operator"" _deg(long double value) {
return value * 3.14159265358979323846L / 180.0L; // Degrees to radians
}
constexpr long double operator"" _rad(long double value) {
return value;
}
int main() {
auto angle1 = 90.0_deg; // π/2 radians
auto angle2 = 1.57_rad; // 1.57 radians
cout << angle1 << ", " << angle2 << endl;
}
Output:
1.5708, 1.57
3. String Literals
#include <string>
std::string operator"" _s(const char* str, size_t len) {
return std::string(str, len);
}
int main() {
auto s1 = "hello"_s; // std::string (not const char*)
auto s2 = "world"; // const char*
// s1.size() works, s2.size() doesn't
cout << s1.size() << endl; // 5
}
Key: "hello"_s creates std::string directly, avoiding implicit conversion.
4. Character Literals
constexpr char operator"" _upper(char c) {
return (c >= 'a' && c <= 'z') ? c - 32 : c;
}
int main() {
char c1 = 'a'_upper; // 'A'
char c2 = 'Z'_upper; // 'Z'
cout << c1 << ", " << c2 << endl;
}
Output:
A, Z
5. Raw Literals
struct Color {
unsigned char r, g, b;
};
Color operator"" _rgb(const char* str, size_t len) {
// Parse hex string like "FF5733"
unsigned int value = std::stoi(str, nullptr, 16);
return Color{
static_cast<unsigned char>((value >> 16) & 0xFF),
static_cast<unsigned char>((value >> 8) & 0xFF),
static_cast<unsigned char>(value & 0xFF)
};
}
int main() {
auto color = "FF5733"_rgb;
cout << "R:" << (int)color.r << " G:" << (int)color.g << " B:" << (int)color.b << endl;
}
Output:
R:255 G:87 B:51
Practical Examples
Example 1: Duration
#include <chrono>
constexpr std::chrono::milliseconds operator"" _ms(unsigned long long value) {
return std::chrono::milliseconds(value);
}
constexpr std::chrono::seconds operator"" _s(unsigned long long value) {
return std::chrono::seconds(value);
}
void wait() {
std::this_thread::sleep_for(100_ms);
std::this_thread::sleep_for(2_s);
}
Note: std::chrono_literals already provides ms, s, min, h suffixes. Use them instead of custom ones.
Example 2: Complex Numbers
#include <complex>
constexpr std::complex<double> operator"" _i(long double value) {
return std::complex<double>(0, value);
}
int main() {
auto z1 = 3.0 + 4.0_i; // 3 + 4i
auto z2 = 1.0_i; // 0 + 1i
cout << z1 << ", " << z2 << endl;
}
Output:
(3,4), (0,1)
Example 3: Binary Literals
constexpr unsigned long long operator"" _b(const char* str, size_t len) {
unsigned long long result = 0;
for (size_t i = 0; i < len; ++i) {
if (str[i] == '1') {
result = (result << 1) | 1;
} else if (str[i] == '0') {
result = result << 1;
}
}
return result;
}
int main() {
auto value = "1010"_b; // 10 in decimal
cout << value << endl;
}
Output:
10
Example 4: JSON Literals
#include <nlohmann/json.hpp>
nlohmann::json operator"" _json(const char* str, size_t len) {
return nlohmann::json::parse(str, str + len);
}
int main() {
auto config = R"({"port": 8080, "host": "localhost"})"_json;
cout << config[port] << endl; // 8080
}
Output:
8080
Common Issues
Issue 1: Name Collision
// ❌ Standard library may add _km later
constexpr long long operator"" _km(unsigned long long value) {
return value * 1000;
}
// ✅ Use namespace
namespace units {
constexpr long long operator"" _km(unsigned long long value) {
return value * 1000;
}
}
using namespace units;
auto d = 10_km;
Key: Put user-defined literals in namespaces to avoid collisions.
Issue 2: Type Mismatch
// ❌ Wrong parameter type
constexpr int operator"" _x(int value) { // Error!
return value * 2;
}
// ✅ Correct parameter type
constexpr int operator"" _x(unsigned long long value) {
return value * 2;
}
Key: Integer literals must use unsigned long long parameter.
Issue 3: Non-constexpr
// ❌ Runtime evaluation
std::string operator"" _upper(const char* str, size_t len) {
std::string result(str, len);
for (auto& c : result) c = std::toupper(c);
return result;
}
// ✅ Compile-time evaluation
constexpr const char* operator"" _upper(const char* str, size_t len) {
// ... constexpr implementation ...
return str;
}
Key: Use constexpr for compile-time evaluation when possible.
Standard Library Literals
C++ standard library provides many useful literals:
chrono Literals
#include <chrono>
using namespace std::chrono_literals;
auto duration1 = 100ms; // milliseconds
auto duration2 = 2s; // seconds
auto duration3 = 5min; // minutes
auto duration4 = 1h; // hours
string Literals
#include <string>
using namespace std::string_literals;
auto s1 = "hello"s; // std::string
auto s2 = "world"sv; // std::string_view (C++17)
complex Literals
#include <complex>
using namespace std::complex_literals;
auto z1 = 3.0 + 4.0i; // std::complex<double>
auto z2 = 1.0if; // std::complex<float>
Production Patterns
Pattern 1: Unit System
namespace units {
struct Distance {
double meters;
constexpr Distance(double m) : meters(m) {}
};
constexpr Distance operator"" _m(long double value) {
return Distance(value);
}
constexpr Distance operator"" _km(long double value) {
return Distance(value * 1000);
}
constexpr Distance operator"" _mi(long double value) {
return Distance(value * 1609.34);
}
}
using namespace units;
void calculateDistance() {
Distance d1 = 10_km;
Distance d2 = 5_mi;
Distance total = Distance(d1.meters + d2.meters);
cout << total.meters << " meters" << endl;
}
Pattern 2: Configuration DSL
struct Config {
int port;
string host;
size_t maxConnections;
};
Config operator"" _config(const char* str, size_t len) {
// Parse config string
// Format: "port:8080,host:localhost,max:100"
Config cfg{8080, "localhost", 100};
// ... parsing logic ...
return cfg;
}
auto serverConfig = "port:8080,host:0.0.0.0,max:1000"_config;
Pattern 3: Compile-Time String Hashing
constexpr size_t hash(const char* str, size_t len) {
size_t h = 0;
for (size_t i = 0; i < len; ++i) {
h = h * 31 + str[i];
}
return h;
}
constexpr size_t operator"" _hash(const char* str, size_t len) {
return hash(str, len);
}
switch (eventType) {
case "click"_hash:
handleClick();
break;
case "hover"_hash:
handleHover();
break;
}
Comparison: Before vs After
Before (Manual Conversion)
Distance d1 = Distance::fromKilometers(10);
Duration t1 = Duration::fromSeconds(5);
Color c1 = Color::fromHex("FF5733");
After (User-Defined Literals)
auto d1 = 10_km;
auto t1 = 5_s;
auto c1 = "FF5733"_rgb;
Benefits:
- More readable
- Less verbose
- Type-safe
- Compile-time checked
Summary
Key Points
- User-defined literals: Custom suffix operators
- operator"": Define literal operators
- Types: Integer, float, string, character, raw
- constexpr: Compile-time evaluation
- Namespace: Avoid name collisions
- Standard library: Use built-in literals when available
Literal Operator Signatures
| Type | Signature |
|---|---|
| Integer | operator"" _x(unsigned long long) |
| Float | operator"" _x(long double) |
| String | operator"" _x(const char*, size_t) |
| Character | operator"" _x(char) |
| Raw | operator"" _x(const char*) |
Best Practices
- Use namespaces to avoid collisions
- Mark
constexprfor compile-time evaluation - Use standard library literals when available
- Document custom literals clearly
- Keep literal operators simple and predictable
When to Use
- Units (distance, time, temperature)
- String types (std::string, custom strings)
- Colors and binary data
- Domain-specific types
- Configuration DSLs
Related Articles
Keywords
C++ user-defined literals, custom suffix operators, operator overloading, type safety, compile-time, constexpr
One-line summary: User-defined literals enable custom suffix operators like 10_km and "hello"_s for expressive, type-safe code with zero runtime overhead.
Frequently Asked Questions (FAQ)
Q. When would I use this in practice?
A. Everything about C++ User-Defined Literals Complete : from basic concepts to practical applications. Master key content …
Q. What should I read before this?
A. Follow the previous article or related articles links at the bottom of each post to learn in sequence. See the C++ series index for the full picture.
Q. Where can I study this more deeply?
A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.
Related Articles (Internal Links)
Other articles related to this topic.
Keywords Covered in This Article (Related Search Terms)
This article covers C++, C++11, user-defined literals, operator overloading, literals, type safety.