跳转至

嵌入式现代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)就是为了解决这个问题——让我们能够定义自己的字面量后缀,比如100ms1kHz1KB等,让代码更直观、更安全。

一句话总结:用户定义字面量允许我们定义自定义的后缀运算符,为字面量添加类型和语义,实现类型安全的单位系统。

在嵌入式开发中,UDL特别有用,因为:

  1. 可以创建类型安全的时间单位(100ms1s
  2. 可以定义物理单位(3.3V1kHz
  3. 可以实现内存单位(1KB1MB
  4. 所有转换都在编译期完成,零运行时开销

我们一步步来看如何实现这些功能。


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)>);