C++ 用户自定义字面量(operator"" _)

发布时间 2023-03-25 17:26:18作者: 明明1109

字面量

字面量是指源码中,固定的常量。比如,

const char* p = "abcd";
const std::string s = "efg";
const int v = 10;
const double d = 20.1;
const unsigned long l = 123465789ul;

等式右边的值,就称为字面量。其中,"abcd","efg"称为字符串字面量,10称为整数字面量,20.1称为浮点数字面量,123465789ul称为无符号长整数字面量。
等式左边变量,是字面量赋值给对应变量的名称。

自定义字面量

假设我们想以1byte为基础,为单位KB, MB, GB, TB, ... ,定义字面量。如果按传统C方式,我们可能需要这样做:

#define BYTE 1
#define KB (1024 * BYTE)
#define MB (1024 * KB)
#define GB (1024 * MB)
#define TB (static_cast<long long>(1024) * GB)

我们知道宏宏虽然速度快,但不安全,因此将其改写成C++风格:

constexpr int BYTE() 
{
    return 1;
}
constexpr int KB()
{
    return 1024 * BYTE();
}
constexpr int MB()
{
    return 1024 * KB();
}
constexpr int GB()
{
    return 1024 * MB();
}
constexpr long long TB()
{
    return static_cast<long long>(1024) * GB();
}

现在有个问题,就是如果我们想要以4KB为基础单位,怎么办?
比如,我们想一次性为一个数组分配4KB大小的存储单元。

std::array<char, 4 * KB()> buf;

能不能把4*放到函数KB()里面?
答案是可以的。C++ 11给了我们更多选择,可以使用operator""实现用户自定义字面量。其用法特点:operator"" _ 标识符,双引号后必须以"_"开头命名的标识符名;如果需要重载,可以传入参数。

constexpr unsigned long long operator"" _KB(unsigned long long k)
{
  return k * 1024;
}
constexpr unsigned long long operator"" _MB(unsigned long long m)
{
  return m * 1024 * 1024;
}
constexpr unsigned long long operator"" _GB(unsigned long long g)
{
  return g * 1024 * 1024 * 1024;
}
constexpr unsigned long long operator"" _TB(unsigned long long g)
{
  return g * 1024 * 1024 * 1024 * 1024;
}

现在,当我们想分配4KB大小数组时,可以这样做:

std::array<char, 4_KB> buf;

再比如,我们可以为时间单位,小时、分钟、秒,定义自定义字面量,用户可以指定具体是多少小时、多少分钟、多少秒,程序无需重新定义类型(函数)。

constexpr std::chrono::hours operator"" _h(unsigned long long h)
{
  return std::chrono::hours(h);
}
constexpr std::chrono::minutes operator"" _min(unsigned long long min)
{
  return std::chrono::minutes(min);
}
constexpr std::chrono::seconds operator"" _s(unsigned long long s)
{
  return std::chrono::seconds(s);
}
constexpr std::chrono::milliseconds operator"" _ms(unsigned long long ms)
{
  return std::chrono::milliseconds(ms);
}

使用时,可以这样表示一天:

constexpr auto OFFSET = 24_h;

这样做的好处:
1)使用函数能进行类型检查,比宏更安全;
2)将单位的运算放在operator函数内部,而不是用户定义处,用户使用起来更方便,不容易写错。

参考

https://github.com/aria2/aria2
https://blog.csdn.net/m0_71009069/article/details/128772813
https://zhuanlan.zhihu.com/p/434508865