嵌入式现代C++开发——用户定义字面量¶
引言¶
你在写嵌入式代码的时候,有没有遇到过这种让人难受的场景?
// 配置定时器:需要把毫秒转换成时钟周期
// 假设系统时钟72MHz,预分频71,定时器时钟1MHz
TIM1->ARR = (1000 - 1); // 1000是什么?毫秒?微秒?
// 配置波特率
USART1->BRR = 0x271; // 这是9600还是115200?
// 配置PWM频率
TIM2->ARR = 72000000 / 1000000 - 1; // 这是要1MHz?
// 内存缓冲区大小
#define BUFFER_SIZE 1024 // 是字节?还是字?
这些"魔数"不仅难以理解,还容易出错。更糟糕的是,不同单位之间的转换完全依赖程序员手动计算,稍有不慎就会出问题。
C++11引入的用户定义字面量(User-Defined Literals,UDL)就是为了解决这个问题——让我们能够定义自己的字面量后缀,比如100ms、1kHz、1KB等,让代码更直观、更安全。
一句话总结:用户定义字面量允许我们定义自定义的后缀运算符,为字面量添加类型和语义,实现类型安全的单位系统。
在嵌入式开发中,UDL特别有用,因为:
- 可以创建类型安全的时间单位(
100ms、1s) - 可以定义物理单位(
3.3V、1kHz) - 可以实现内存单位(
1KB、1MB) - 所有转换都在编译期完成,零运行时开销
我们一步步来看如何实现这些功能。
UDL基础语法¶
operator""的定义形式¶
用户定义字面量通过operator""后缀运算符来定义。根据参数类型的不同,有几种定义形式:
// 整型字面量( cooked)
ReturnType operator"" _suffix(unsigned long long int value);
// 浮点型字面量(cooked)
ReturnType operator"" _suffix(long double value);
// 字符串字面量(原始)
ReturnType operator"" _suffix(const char* str, size_t length);
// 字符字面量(cooked)
ReturnType operator"" _suffix(char c);
// 宽字符版本
ReturnType operator"" _suffix(const wchar_t* str, size_t length);
ReturnType operator"" _suffix(const char16_t* str, size_t length);
ReturnType operator"" _suffix(const char32_t* str, size_t length);
第一个UDL示例¶
让我们从最简单的例子开始:
// 定义距离单位
struct Distance {
long double meters;
};
// 定义米字面量
constexpr Distance operator"" _m(long double value) {
return Distance{value};
}
// 定义千米字面量
constexpr Distance operator"" _km(long double value) {
return Distance{value * 1000.0};
}
// 定义厘米字面量
constexpr Distance operator"" _cm(long double value) {
return Distance{value / 100.0};
}
// 使用
void test_distance() {
auto d1 = 1.5_m; // 1.5米
auto d2 = 2.0_km; // 2000米
auto d3 = 100.0_cm; // 1米
// 编译期计算
constexpr auto total = 1.0_km + 500.0_m; // 1500米
static_assert(total.meters == 1500.0);
}
重要:UDL后缀必须以下划线开头。不带下划线的后缀(如1ms)是保留给标准库的。
整型与浮点型重载¶
你可以为整型和浮点型分别定义重载:
// 浮点版本
constexpr Frequency operator"" _Hz(long double value) {
return Frequency{static_cast<uint32_t>(value)};
}
// 整数版本
constexpr Frequency operator"" _Hz(unsigned long long value) {
return Frequency{static_cast<uint32_t>(value)};
}
// 使用
auto f1 = 100_Hz; // 调用整型版本
auto f2 = 1.5_kHz; // 调用浮点版本
原始字面量与Cooked字面量¶
Cooked字面量¶
"Cooked"字面量是指编译器已经解析并转换后的字面量。对于整型和浮点型字面量,编译器会先把它们转换成数值类型:
// Cooked - 编译器已经把"123"解析成整数123
constexpr int operator"" _dec(unsigned long long value) {
return static_cast<int>(value * 10);
}
auto x = 123_dec; // value = 123, x = 1230
原始字面量¶
原始字面量接收的是原始字符串,编译器不做任何解析:
// 原始字面量 - 接收原始字符序列
constexpr Binary operator"" _bin(const char* str, size_t length) {
unsigned long long value = 0;
for (size_t i = 0; i < length; ++i) {
if (str[i] == '0') {
value = value * 2;
} else if (str[i] == '1') {
value = value * 2 + 1;
}
}
return Binary{value};
}
// 使用
auto b1 = 1010_bin; // str = "1010", length = 4, value = 10
auto b2 = 11111111_bin; // = 255
原始字面量的优势在于可以处理编译器不支持的格式,比如二进制字面量(在C++14之前)。
混合使用¶
// Cooked版本 - 用于十进制十六进制
constexpr uint32_t operator"" _U(unsigned long long value) {
return static_cast<uint32_t>(value);
}
// 原始版本 - 用于二进制
constexpr uint32_t operator"" _U(const char* str, size_t length) {
uint32_t value = 0;
for (size_t i = 0; i < length; ++i) {
value = value * 10 + (str[i] - '0');
}
return value;
}
// 使用
auto x1 = 42_U; // 使用cooked版本,x1 = 42
auto x2 = 0xFF_U; // 使用cooked版本,x2 = 255
auto x3 = 1010_U; // 使用原始版本,x3 = 1010(不是十进制!)
// 注意:对于整数字面量,优先匹配cooked版本
// 如果字符串中只有数字,两个版本都可能匹配,编译器会选择更匹配的
内置字面量回顾¶
C++标准库已经提供了一些有用的字面量后缀。在使用UDL之前,先了解一下内置的字面量:
chrono字面量(C++14)¶
#include <chrono>
using namespace std::chrono_literals;
// 时间字面量
auto t1 = 1s; // std::chrono::seconds
auto t2 = 500ms; // std::chrono::milliseconds
auto t3 = 2us; // std::chrono::microseconds
auto t4 = 100ns; // std::chrono::nanoseconds
auto t5 = 1min; // std::chrono::minutes
auto t6 = 1h; // std::chrono::hours
// 时间运算
auto total = 1s + 500ms; // 1500ms
auto ratio = 1s / 1ms; // 1000(单位比例)
string字面量(C++14)¶
#include <string>
using namespace std::string_literals;
// string字面量
auto s1 = "hello"s; // std::string
auto s2 = L"wide"s; // std::wstring
auto s3 = u"utf16"s; // std::u16string
auto s4 = U"utf32"s; // std::u32string
// 原始string字面量
auto s5 = R"(raw
string
literal)"s;
complex字面量(C++14)¶
#include <complex>
using namespace std::complex_literals;
// 复数字面量
auto c1 = 3.0 + 4.0i; // std::complex<double>
auto c2 = 1.0i; // std::complex<double>(0, 1)
注意:这些标准库字面量不需要下划线前缀,因为它们在std::literals命名空间内。
嵌入式场景实战¶
场景1:时间单位系统¶
在嵌入式系统中,延时和定时器配置是最常见的场景。让我们创建一个完整的时间单位系统:
#include <cstdint>
#include <chrono>
// 时间单位基类
template<uint64_t Period>
struct Duration {
uint64_t ticks;
static constexpr uint64_t period = Period; // 纳秒为单位的周期
// 转换为纳秒
constexpr uint64_t to_nanoseconds() const {
return ticks * period;
}
// 转换为微秒
constexpr uint64_t to_microseconds() const {
return ticks * period / 1000;
}
// 转换为毫秒
constexpr uint64_t to_milliseconds() const {
return ticks * period / 1000000;
}
// 转换为秒
constexpr uint64_t to_seconds() const {
return ticks * period / 1000000000;
}
};
// 类型别名
using Nanoseconds = Duration<1>;
using Microseconds = Duration<1000>;
using Milliseconds = Duration<1000000>;
using Seconds = Duration<1000000000>;
// 字面量定义
constexpr Nanoseconds operator"" _ns(unsigned long long value) {
return Nanoseconds{value};
}
constexpr Microseconds operator"" _us(unsigned long long value) {
return Microseconds{value};
}
constexpr Milliseconds operator"" _ms(unsigned long long value) {
return Milliseconds{value};
}
constexpr Seconds operator"" _s(unsigned long long value) {
return Seconds{value};
}
// 时间单位的运算
template<uint64_t P1, uint64_t P2>
constexpr auto operator+(Duration<P1> a, Duration<P2> b) {
// 转换为纳秒相加,然后转换为最小单位
constexpr uint64_t min_period = (P1 < P2) ? P1 : P2;
return Duration<min_period>{a.to_nanoseconds() / min_period +
b.to_nanoseconds() / min_period};
}
// 使用示例
void test_durations() {
// 编译期计算
constexpr auto t1 = 1_s + 500_ms; // 1500ms
constexpr auto t2 = 1000_us + 500_us; // 1500us
constexpr auto t3 = 1_s - 500_ms; // 500ms
static_assert(t1.to_milliseconds() == 1500);
static_assert(t2.to_microseconds() == 1500);
static_assert(t3.to_milliseconds() == 500);
}
场景2:定时器配置¶
结合时间单位,我们可以创建类型安全的定时器配置:
class TimerConfig {
public:
template<uint64_t Period>
constexpr TimerConfig(Duration<Period> period, uint32_t clock_hz)
: arr_(calculate_arr(period, clock_hz))
, psc_(calculate_psc(period, clock_hz)) {}
constexpr uint32_t arr() const { return arr_; }
constexpr uint32_t psc() const { return psc_; }
private:
uint32_t arr_;
uint32_t psc_;
static constexpr uint32_t calculate_psc(auto duration, uint32_t clock_hz) {
// 目标:找到合适的预分频器,使定时器时钟在合理范围
// 假设我们想要定时器时钟约为1MHz
if (clock_hz > 1000000) {
return (clock_hz / 1000000) - 1;
}
return 0;
}
static constexpr uint32_t calculate_arr(auto duration, uint32_t clock_hz) {
uint64_t period_ns = duration.to_nanoseconds();
uint64_t timer_clock = clock_hz / (psc_ + 1); // Hz
uint64_t tick_period_ns = 1000000000 / timer_clock; // 纳秒
return static_cast<uint32_t>(period_ns / tick_period_ns) - 1;
}
static constexpr uint32_t psc_ = 0; // 简化示例
};
// 使用
constexpr auto timer1 = TimerConfig(100_ms, 72000000); // 72MHz时钟
// timer1.arr() 和 timer1.psc() 可以直接写入寄存器
// 实际应用
void configure_timer() {
TIM1->PSC = timer1.psc();
TIM1->ARR = timer1.arr();
TIM1->CR1 |= TIM_CR1_CEN;
}
场景3:频率字面量¶
struct Frequency {
uint32_t hz;
// 转换方法
constexpr uint32_t to_hz() const { return hz; }
constexpr uint32_t to_khz() const { return hz / 1000; }
constexpr uint32_t to_mhz() const { return hz / 1000000; }
// 频率转周期(纳秒)
constexpr uint64_t period_ns() const {
return 1000000000ULL / hz;
}
};
// 频率字面量
constexpr Frequency operator"" _Hz(unsigned long long value) {
return Frequency{static_cast<uint32_t>(value)};
}
constexpr Frequency operator"" _kHz(long double value) {
return Frequency{static_cast<uint32_t>(value * 1000.0)};
}
constexpr Frequency operator"" _MHz(long double value) {
return Frequency{static_cast<uint32_t>(value * 1000000.0)};
}
// 使用
void test_frequency() {
// 波特率配置
constexpr auto baudrate = 115200_Hz;
USART1->BRR = calculate_baud_register(baudrate, 72000000_Hz);
// PWM频率配置
constexpr auto pwm_freq = 20_kHz; // 20kHz PWM
constexpr auto timer_period = pwm_freq.period_ns(); // 50000ns
// 时钟配置
constexpr auto sysclock = 72_MHz;
constexpr auto apb1clock = 36_MHz;
}
// 波特率寄存器计算
constexpr uint16_t calculate_baud_register(Frequency baud, Frequency clock) {
return static_cast<uint16_t>(
static_cast<double>(clock.to_hz()) / (16.0 * baud.to_hz())
);
}
场景4:内存大小字面量¶
struct MemorySize {
uint64_t bytes;
constexpr uint64_t to_bytes() const { return bytes; }
constexpr uint32_t to_kibibytes() const { return bytes / 1024; }
constexpr uint32_t to_mebibytes() const { return bytes / (1024 * 1024); }
};
// 内存字面量(使用KiB、MiB等IEC标准单位)
constexpr MemorySize operator"" _B(unsigned long long value) {
return MemorySize{value};
}
constexpr MemorySize operator"" _KiB(unsigned long long value) {
return MemorySize{value * 1024};
}
constexpr MemorySize operator"" _MiB(unsigned long long value) {
return MemorySize{value * 1024 * 1024};
}
// 使用
void buffer_config() {
// 编译期分配缓冲区大小
constexpr auto buffer_size = 4_KiB;
uint8_t buffer[static_cast<size_t>(buffer_size.to_bytes())];
// Flash大小检查
constexpr auto flash_size = 512_KiB;
constexpr auto app_size = 256_KiB;
static_assert(app_size.to_bytes() < flash_size.to_bytes(),
"Application too large for flash!");
// RAM检查
constexpr auto ram_size = 128_KiB;
constexpr auto stack_size = 4_KiB;
constexpr auto heap_size = 32_KiB;
static_assert((stack_size + heap_size).to_bytes() < ram_size.to_bytes(),
"Not enough RAM!");
}
// 内存池配置
template<MemorySize Size>
class MemoryPool {
static constexpr size_t pool_size = static_cast<size_t>(Size.to_bytes());
std::array<uint8_t, pool_size> buffer_;
public:
constexpr size_t capacity() const { return pool_size; }
void* allocate(size_t size) {
// 简化的分配逻辑
return (size <= pool_size) ? buffer_.data() : nullptr;
}
};
// 使用
MemoryPool<2_KiB> small_pool;
MemoryPool<16_KiB> large_pool;
场景5:电压字面量¶
struct Voltage {
long double millivolts;
constexpr long double to_volts() const {
return millivolts / 1000.0;
}
constexpr long double to_millivolts() const {
return millivolts;
}
// ADC值转换
constexpr uint16_t to_adc_value(uint32_t vref_mv, uint16_t adc_resolution) const {
return static_cast<uint16_t>(
(millivolts * adc_resolution) / vref_mv
);
}
};
// 电压字面量
constexpr Voltage operator"" _V(long double value) {
return Voltage{value * 1000.0};
}
constexpr Voltage operator"" _mV(long double value) {
return Voltage{value};
}
// 使用
void voltage_example() {
constexpr auto vref = 3.3_V; // 参考电压
constexpr auto vdd = 3.0_V; // 供电电压
constexpr auto threshold = 1.2_V; // 阈值电压
// 检查电压范围
static_assert(vdd.to_volts() > 2.7 && vdd.to_volts() < 3.6,
"VDD out of range!");
// ADC转换
constexpr uint16_t adc_12bit = 4095;
constexpr auto sensor_voltage = 1.65_V;
constexpr auto adc_value = sensor_voltage.to_adc_value(
vref.to_millivolts(), adc_12bit
); // ~2047
// DAC值设置
constexpr auto dac_output = 1.5_V;
DAC->DHR12R1 = dac_output.to_adc_value(vref.to_millivolts(), adc_12bit);
}
场景6:类型安全的单位系统(完整示例)¶
#include <cstdint>
#include <type_traits>
// 单位标签
struct HertzTag {};
struct MeterTag {};
struct SecondTag {};
// 带单位的值
template<typename T, typename Unit>
struct Quantity {
T value;
constexpr explicit Quantity(T v) : value(v) {}
// 单位保持不变的运算
constexpr Quantity operator+(Quantity other) const {
return Quantity(value + other.value);
}
constexpr Quantity operator-(Quantity other) const {
return Quantity(value - other.value);
}
constexpr Quantity operator*(T scalar) const {
return Quantity(value * scalar);
}
// 单位转换的除法(频率 * 时间 = 无量纲)
template<typename OtherUnit>
constexpr auto operator*(Quantity<T, OtherUnit> other) const {
// 复杂的单位系统需要更详细的traits定义
return Quantity<decltype(value * other.value), void>{value * other.value};
}
constexpr Quantity operator/(T scalar) const {
return Quantity(value / scalar);
}
};
// 时间单位
using Time = Quantity<uint64_t, SecondTag>;
constexpr Time operator"" _s(unsigned long long value) {
return Time(value);
}
constexpr Time operator"" _ms(unsigned long long value) {
return Time(value * 1000000); // 转换为基准单位(纳秒)
}
// 频率单位
using Frequency = Quantity<uint32_t, HertzTag>;
constexpr Frequency operator"" _Hz(unsigned long long value) {
return Frequency(static_cast<uint32_t>(value));
}
constexpr Frequency operator"" _kHz(unsigned long long value) {
return Frequency(static_cast<uint32_t>(value * 1000));
}
// 距离单位
using Distance = Quantity<long double, MeterTag>;
constexpr Distance operator"" _m(long double value) {
return Distance(value);
}
constexpr Distance operator"" _cm(long double value) {
return Distance(value / 100.0);
}
constexpr Distance operator"" _mm(long double value) {
return Distance(value / 1000.0);
}
// 使用
void unit_system_example() {
// 类型安全:不能把时间和距离相加
auto t1 = 100_ms;
auto d1 = 50.0_mm;
// d1 = t1; // 编译错误!类型不同
// 正确的单位运算
auto t2 = 1_s + 500_ms;
auto d2 = 10.0_cm + 5.0_mm;
// 速度 = 距离 / 时间
auto speed = d2.value / t2.value; // m/s
}
编译期计算¶
UDL配合constexpr可以实现纯编译期的单位转换系统,零运行时开销。
编译期断言¶
constexpr auto system_clock = 72_MHz;
constexpr auto apb1_clock = 36_MHz;
constexpr auto apb2_clock = 72_MHz;
// 编译期验证时钟配置
static_assert(system_clock.to_hz() == 72000000, "System clock mismatch");
static_assert(apb1_clock.to_hz() == system_clock.to_hz() / 2,
"APB1 clock should be half of system clock");
// 编译期验证定时器参数
constexpr auto timer_period = 100_ms;
constexpr auto timer_ticks = (system_clock.to_hz() / 10000) * timer_period.to_milliseconds();
static_assert(timer_ticks < 65536, "Timer period too large for 16-bit timer!");
编译期查表¶
// 预分频器值查表
constexpr uint16_t prescaler_values[] = {1, 2, 4, 8, 16, 64, 256, 1024};
// 编译期查找合适的预分频器
constexpr uint16_t find_prescaler(uint32_t source_freq, uint32_t target_freq) {
for (auto psc : prescaler_values) {
if (source_freq / psc <= target_freq) {
return psc - 1; // 寄存器值为分频系数-1
}
}
return 1023; // 最大分频
}
// 使用
constexpr auto source = 16_MHz;
constexpr auto target = 1_kHz;
constexpr auto psc = find_prescaler(source.to_hz(), target.to_hz());
static_assert(psc == 15, "Prescaler calculation error"); // 16MHz/16 = 1MHz
// 编译期计算ARR值
constexpr uint32_t arr_value = (source.to_hz() / (psc + 1)) / target.to_hz() - 1;
static_assert(arr_value == 9999, "ARR calculation error"); // 1MHz/1000 - 1
编译期单位转换¶
// 编译期波特率计算
constexpr uint16_t compute_baud(uint32_t peripheral_clock, uint32_t baud_rate) {
return static_cast<uint16_t>(
(peripheral_clock + baud_rate / 2) / baud_rate // 四舍五入
);
}
// 使用
constexpr auto uart_clock = 72_MHz;
constexpr auto baud115200 = 115200_Hz;
constexpr auto brr_115200 = compute_baud(
uart_clock.to_hz() / 16, // USART时钟通常为PCLK2/16
baud115200.to_hz()
);
static_assert(brr_115200 == 39, "BRR calculation for 115200");
constexpr auto baud9600 = 9600_Hz;
constexpr auto brr_9600 = compute_baud(
uart_clock.to_hz() / 16,
baud9600.to_hz()
);
static_assert(brr_9600 == 468, "BRR calculation for 9600");
常见的坑和注意事项¶
坑1:UDL后缀命名冲突¶
// 在头文件中定义UDL
namespace MyLib {
constexpr int operator"" _deg(long double value) {
return static_cast<int>(value);
}
}
// 使用
using namespace MyLib;
auto angle = 45.0_deg; // OK
// 如果另一个库也定义了_deg
namespace OtherLib {
constexpr int operator"" _deg(long double value) {
return static_cast<int>(value + 0.5); // 不同的实现!
}
}
using namespace OtherLib;
auto angle2 = 45.0_deg; // 二义性错误或使用错误的实现
// 解决方法:使用明确的命名空间
auto angle3 = MyLib::operator"" _deg(45.0);
// 或者避免using namespace
建议:为UDL使用独特的前缀,如_mdeg(my degrees)。
坑2:运算优先级¶
// ❌ 错误理解:以为会先乘后除
auto x = 100_km / 2 * 3;
// 实际:从左到右计算
// (100_km / 2) * 3
// = 50_km * 3 = 150_km
// 如果想要 100_km / (2 * 3),需要加括号
auto y = 100_km / (2 * 3);
坑3:整数溢出¶
// 大数字字面量
constexpr auto huge = 10000000000000000000_UL; // 可能溢出unsigned long long
// 原始字符串版本可以检查
constexpr uint64_t operator"" _big(const char* str, size_t length) {
// 可以添加溢出检查
uint64_t result = 0;
for (size_t i = 0; i < length; ++i) {
if (result > UINT64_MAX / 10) {
throw "Overflow"; // 编译期错误
}
result = result * 10 + (str[i] - '0');
}
return result;
}
// constexpr auto test = 99999999999999999999_big; // 编译错误
坑4:浮点精度¶
// 浮点UDL可能有精度问题
constexpr auto value1 = 0.1_V + 0.2_V; // 可能不等于0.3_V
constexpr auto value2 = 0.3_V;
// static_assert(value1.millivolts == value2.millivolts); // 可能失败!
// 解决方法:使用整数表示(毫伏而不是伏)
struct VoltageMilli {
int64_t millivolts;
};
constexpr VoltageMilli operator"" _V_milli(long double value) {
return VoltageMilli{static_cast<int64_t>(value * 1000.0 + 0.5)}; // 四舍五入
}
constexpr auto v1 = 0.1_V_milli + 0.2_V_milli;
constexpr auto v2 = 0.3_V_milli;
static_assert(v1.millivolts == v2.millivolts); // OK
坑5:全局命名空间污染¶
// ❌ 不要在全局命名空间定义UDL
constexpr int operator"" _custom(long double x) {
return static_cast<int>(x);
}
// 这会影响所有包含此头文件的代码
// ✅ 放在命名空间内
namespace MyProject {
constexpr int operator"" _custom(long double x) {
return static_cast<int>(x);
}
// 或者使用inline命名空间(C++20)
inline namespace literals {
constexpr int operator"" _custom(long double x) {
return static_cast<int>(x);
}
}
}
// 使用时
using namespace MyProject::literals;
auto x = 42.0_custom;
坑6:UDL不能用于字符串字面量的某些操作¶
// ❌ UDL不能修改字符串的内容
std::string operator"" _upper(const char* str, size_t length) {
std::string result(str, length);
for (auto& c : result) {
c = std::toupper(c); // 这是OK的
}
return result;
}
// 但是不能这样用:
// auto s = "hello"_upper; // 这会创建一个新的string,运行时执行
// 不是编译期操作(除非constexpr)
// ✅ 如果需要编译期字符串处理,需要更复杂的实现
C++11/14/17/20的更新¶
C++11:引入UDL¶
// C++11的基本UDL
constexpr int operator"" _square(unsigned long long value) {
return value * value;
}
auto x = 5_square; // 25
C++14:更宽松的constexpr¶
// C++14允许更复杂的constexpr函数
constexpr long double operator"" _pi(long double) {
return 3.14159265358979323846L;
}
constexpr auto area = 10.0_m * 10.0_m * 1.0_pi; // 编译期计算圆面积
C++17:if constexpr¶
// 使用if constexpr在编译期选择不同的实现
template<typename T>
constexpr auto check_units(T value) {
if constexpr (std::is_same_v<T, Frequency>) {
return value.to_hz();
} else if constexpr (std::is_same_v<T, Time>) {
return value.to_milliseconds();
} else {
return value;
}
}
constexpr auto f = 100_kHz;
static_assert(check_units(f) == 100000);
C++17:类模板参数推导(CTAD)¶
// CTAD简化UDL的使用
template<typename T>
struct Value {
T v;
constexpr Value(T x) : v(x) {}
};
// 不需要显式指定模板参数
Value operator"" _val(unsigned long long x) {
return Value{x};
}
auto v = 42_val; // 推导为 Value<unsigned long long>
C++20:constexpr增强¶
// C++20允许更多操作在constexpr中
constexpr std::string operator"" _concat(const char* str, size_t len) {
return std::string(str, len); // C++20中constexpr可以调用std::string构造
}
// C++20的consteval强制编译期执行
consteval int operator"" _compile(unsigned long long value) {
return static_cast<int>(value * 2);
}
constexpr auto x = 100_compile; // OK
// int y; std::cin >> y;
// auto z = y_compile; // 错误!不是编译期常量
C++20: Concepts约束¶
#include <concepts>
// 使用Concepts约束UDL
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;
template<Numeric T>
struct Quantity {
T value;
};
constexpr auto operator"" _units(long double value) {
return Quantity<long double>{value};
}
// 只接受数值类型
static_assert(Numeric<decltype(1.0_units.value)>);