在现代编程中, 代码的可读性和类型安全变得越来越重要. C++ 自 C++11 开始引入了一系列新特性, 使开发者能够以更加直观的方式表示数据, 从而减少隐式转换错误并提升代码可维护性.
环境要求
本文中的代码需要编译器支持 C++23 标准才能通过.
数值
二进制、八进制、十六进制字面量
符号 | 类型 | 样例 |
---|---|---|
0b 或 0B | 二进制 | 0b1111 |
0 | 八进制 | 017 |
0x 或 0X | 十六进制 | 0xF |
// 不同进制字面量
constexpr int base2 = 0b1111; // 二进制 15
constexpr int base8 = 017; // 8进制 15
constexpr int base10 = 15; // 十进制 15
constexpr int base16 = 0xf; // 16进制 15
// 检查
static_assert(base2 == 15);
static_assert(base8 == 15);
static_assert(base10 == 15);
static_assert(base16 == 15);
单引号作为数值分隔符
C++14 引入了单引号作为数值分隔符,可以提高可读性.
// 单引号作为数值分隔符
constexpr int a = 1'000'000; // 1000000, 十进制
static_assert(a == 1000000);
// 同样适用于其他进制单位
constexpr int c = 0b1'0000; // 16, 二进制
static_assert(c == 16);
constexpr int d = 01'0000; // 4096, 八进制
static_assert(d == 4096);
constexpr int b = 0x1'0000; // 65536, 十六进制
static_assert(b == 65536);
修饰存储类型
符号 | 类型 | 样例 |
---|---|---|
u 或 U | unsigned | 1u |
l 或 L | long | 1l |
ll 或 LL | long long | 1ll |
z | std::size_t | 0z |
// 修饰存储类型
#include <type_traits>
auto si = 1; // int, 默认
static_assert(std::is_same_v<decltype(si), int>);
auto ui = 1u; // unsigned int
static_assert(std::is_same_v<decltype(ui), unsigned int>);
auto ul = 1ul; // unsigned long
static_assert(std::is_same_v<decltype(ul), unsigned long>);
auto ull = 1ull; // unsigned long long
static_assert(std::is_same_v<decltype(ull), unsigned long long>);
auto l = 0l; // long
static_assert(std::is_same_v<decltype(l), long>);
auto ll = 0ll; // long long
static_assert(std::is_same_v<decltype(ll), long long>);
auto z = 0z; // C++23 引入, 定义带符号的 size_t 类型,特别适用于循环中需要比较有符号和无符号值的场景
static_assert(std::is_same_v<decltype(z), std::ptrdiff_t>);
auto uz = 0uz; // from C++23
static_assert(std::is_same_v<decltype(uz), std::size_t>);
为什么引入z
和uz
?
C++23 引入了z
和uz
修饰符, 用于带符号的size_t
类型, 解决有符号数与无符号数比较的警告问题.
std::vector<int> vec = {1, 2, 3, 4, 5};
// 传统代码可能会出现如下警告:
// warning: comparison between signed and unsigned integer expressions
for (auto i = 0; i < vec.size(); ++i) {
// do something
}
// 通过 uz 后缀可以避免:
for (auto i = 0uz; i < vec.size(); ++i) {
// do something
}
浮点数字面量修饰符:
符号 | 类型 | 样例 |
---|---|---|
f 或 F | float | 1.0f |
l 或 L | long double | 1.0l |
// 修饰浮点数
auto f = 1.0f; // float
static_assert(std::is_same_v<decltype(f), float>);
auto d = 1.0; // double, 默认
static_assert(std::is_same_v<decltype(d), double>);
auto ld = 1.0l; // long double
static_assert(std::is_same_v<decltype(ld), long double>);
科学计数法
// 科学计数法
constexpr double e = 1e3; // 1000.0
static_assert(e == 1000.0);
constexpr auto E = 1E3; // 1000.0
static_assert(E == 1000.0);
字符字面量
符号 | 类型 | 样例 |
---|---|---|
u | char16_t | u'a' |
U | char32_t | U'a' |
L | wchar_t | L'汉' |
u8 | char8_t | u8'a' |
// 修饰字符
auto c = 'a'; // char, 默认
static_assert(std::is_same_v<decltype(c), char>);
auto wc = L'汉'; // wchar_t
static_assert(std::is_same_v<decltype(wc), wchar_t>);
auto u8c = u8'a'; // char8_t
static_assert(std::is_same_v<decltype(u8c), char8_t>);
auto u = u'a'; // char16_t
static_assert(std::is_same_v<decltype(u), char16_t>);
auto U = U'a'; // char32_t
static_assert(std::is_same_v<decltype(U), char32_t>);
字符串字面量
原始字符串字面量
对于多行字符串, 可以使用原始字符串字面量, 这样就不需要转义字符了.
// 原始字符串字面量
auto json = R"({"key": "value"})";
static_assert(std::is_same_v<decltype(json), const char*>);
std::string
字面量
C++14 引入了std::string
字面量, 可以直接使用字符串字面量初始化std::string
对象.
#include <string>
using namespace std::string_literals; // C++14
auto str = "hello"s; // std::string, 注意这里的 s
auto ptr = "hello"; // const char*
static_assert(std::is_same_v<decltype(str), std::string>);
static_assert(std::is_same_v<decltype(ptr), const char*>);
utf-8 字符串字面量
C++17 引入了utf-8
字符串字面量, 可以直接使用utf-8
字符串字面量初始化std::string
对象.
// utf-8 字符串字面量
auto utf8 = u8"你好";
static_assert(std::is_same_v<decltype(utf8), const char*>);
其他字面量
布尔字面量
// 布尔字面量
auto boolean = true; // bool, 默认
static_assert(std::is_same_v<decltype(boolean), bool>);
auto b2 = false; // bool
static_assert(std::is_same_v<decltype(b2), bool>);
空指针字面量
// 空指针字面量
auto null = nullptr; // std::nullptr_t, 默认
static_assert(std::is_same_v<decltype(null), std::nullptr_t>);
用户定义的字面量(User-Defined Literals, UDL)
C++11 引入了用户定义的字面量(User-Defined Literals, UDL), 允许程序员为自定义类型定义自己的字面量表示. 这通过定义一个字面量运算符实现, 运算符以 _
开头, 后跟一个或多个字符. 用户定义字面量仅支持以下基础类型: int
, unsigned int
, long
, unsigned long
, float
, double
, long double
, char
, const char*
.
以下是一个简单的例子,定义一个用于表示长度的用户定义字面量:
#include <iostream>
// 定义一个表示长度的结构体
struct Length {
double value; // 长度值
enum Unit { meter, kilometer, centimeter }; // 单位
Unit unit; // 长度单位
// 构造函数
Length(double val, Unit u) : value(val), unit(u) {}
};
// 用户定义的字面量,用于米
Length operator"" _m(long double val) {
return Length(val, Length::meter);
}
// 用户定义的字面量,用于千米
Length operator"" _km(long double val) {
return Length(val, Length::kilometer);
}
// 用户定义的字面量,用于厘米
Length operator"" _cm(long double val) {
return Length(val, Length::centimeter);
}
int main() {
Length length1 = 10.0_m; // 10米
Length length2 = 5.0_km; // 5千米
Length length3 = 100.0_cm; // 100厘米
std::cout << "Length1: " << length1.value << " meters\n";
std::cout << "Length2: " << length2.value << " kilometers\n";
std::cout << "Length3: " << length3.value << " centimeters\n";
return 0;
}
结论与展望
现代 C++ 的字面量特性让开发者能够更加自然、直观地表示数据, 并提供了丰富的扩展能力. 结合用户定义字面量, 开发者可以将其应用到科学计算、图形编程、国际化支持等场景, 进一步提升代码的可读性和维护性.
源码
完整的样例在这里.