C++23 不仅带来了 std::expected、if consteval 这样的”大新闻”,还升级了一系列日常工具函数。std::print 让输出更简洁,std::to_underlying 让类型转换更清晰,contains() 让查找更直观。本文为你盘点这些”小而美”的改进。
一、std::print:一行搞定格式化输出
传统方式的繁琐
1 2 3 4 5
| #include <format> #include <iostream>
std::cout << std::format("Hello {}! int={}, hex={:#x}\n", "World", 42, 42);
|
std::format 解决了”格式化字符串”的问题,但最终还是要 << std::cout。C++23 的 std::print 直接一步到位:
1 2 3 4 5
| #include <print>
std::print("Hello {}!\n", "World"); std::print("int={:d}, hex={:#x}, float={:.2f}\n", 42, 42, 3.14159); std::print(stderr, "Error occurred\n");
|
std::print 的优势
| 特性 | 说明 |
|---|
| 自动支持 chrono | std::print("{:{%H:%M:%S}}", now); 无需手动转换 |
| 类型安全 | 编译期检查格式,与 std::format 相同 |
| 性能优异 | 直接写入缓冲区,避免 operator<< 的虚拟调用开销 |
| FILE 支持* | std::print(stderr, ...) 比 fprintf 更安全 |
chrono 集成示例
1 2 3 4 5
| #include <print> #include <chrono>
auto now = std::chrono::system_clock::now(); std::print("Current time: {:%Y-%m-%d %H:%M:%S}\n", now);
|
二、std::to_underlying:类型转换更清晰
痛点
C++11 引入了 std::underlying_type,但使用起来需要两步:
1 2 3 4 5 6 7 8
| enum Color : int { Red = 1, Green, Blue };
Color c = Color::Red;
int underlying = static_cast<std::underlying_type_t<Color>>(c);
|
C++23 解决方案
1 2 3 4
| #include <type_traits>
Color c = Color::Red; int underlying = std::to_underlying(c);
|
std::to_underlying(E e) 等价于 static_cast<std::underlying_type_t<E>>(e),但:
- 更短
- 不容易与
const_cast / reinterpret_cast 混淆 - 意图明确:”提取底层类型值”
三、std::embed:C++26 的特性(本文勘误)
⚠️ 勘误说明:部分早期资料将 std::embed 归为 C++23,但该特性已被推迟到 C++26。
std::embed 用于在编译期将文件内容嵌入为 std::array<char, N>:
1 2 3 4 5
| #include <embed>
constexpr auto shader_code = std::embed("shader.frag"); static_assert(shader_code.size() > 0);
|
C++23 的实用工具主要还是 std::to_underlying 和 std::print。
四、contains():查找再也不需要 != end()
std::map / std::unordered_map
1 2 3 4 5 6 7
| std::map<int, std::string> m{{1,"one"}, {2,"two"}};
if (m.find(1) != m.end()) { }
if (m.contains(1)) { }
|
std::vector / std::list
1 2 3 4 5 6 7
| std::vector<int> v{1, 2, 3};
if (std::find(v.begin(), v.end(), 2) != v.end()) { }
if (v.contains(2)) { }
|
std::string / std::string_view
1 2 3 4 5 6 7
| std::string_view sv = "hello world";
if (sv.contains("world")) { }
if (sv.starts_with("hello")) { }
|
五、完整示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include <iostream> #include <print> #include <format> #include <map> #include <vector> #include <string_view>
enum Color : int { Red = 1, Green, Blue };
int main() { std::print("Hello {}!\n", "World"); std::print("int={:d}, hex={:#x}, float={:.2f}\n", 42, 42, 3.14159); std::print(stderr, "Error occurred\n"); Color c = Color::Red; int underlying = static_cast<int>(c); (void)underlying; std::map<int, std::string> m{{1,"one"}, {2,"two"}}; if (m.contains(1)) std::cout << "m has key 1\n"; std::vector<int> v{1, 2, 3}; if (v.contains(2)) std::cout << "v has 2\n"; std::string_view sv = "hello world"; if (sv.contains("world")) std::cout << "found\n"; }
|
输出:
1 2 3 4 5 6
| Hello World! int=42, hex=0x2a, float=3.14 Error occurred m has key 1 v has 2 found
|
六、C++23 其他小工具一览
| 特性 | 说明 |
|---|
std::views::chunk_by() | 按谓词分组视图 |
std::flat_map | 键值对容器,底层是 vector |
std::flat_set | 集合容器,底层是 vector |
std::views::join_with | 用分隔符连接视图 |
[[assume(expr)]] | 提示编译器表达式为真,启用更多优化 |
总结:C++23 的这些工具函数虽然不如 std::expected、co_await 那样重磅,但它们直接提升了日常编码体验。std::print 让输出代码从 3 行变 1 行,contains() 让布尔表达式更符合自然语言习惯,to_underlying 让类型转换意图更清晰。这些改进值得你在下一个项目中也用起来。
📚 C++23 新特性 系列导航
本文是《C++23 新特性》系列第 3/4 篇。
📖 全部 4 篇目录(点击展开)
- (一)std::expected
- (二)if consteval 与 Deducing this
- (三)std::print / to_underlying ← 当前
- (四)Ranges 增强