【C++23】(三)std::print / std::to_underlying / std::embed:工具箱大升级

C++23 不仅带来了 std::expectedif consteval 这样的”大新闻”,还升级了一系列日常工具函数。std::print 让输出更简洁,std::to_underlying 让类型转换更清晰,contains() 让查找更直观。本文为你盘点这些”小而美”的改进。


一、std::print:一行搞定格式化输出

传统方式的繁琐

1
2
3
4
5
// C++20: std::format + std::cout
#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"); // 支持输出到任意 FILE*

std::print 的优势

特性说明
自动支持 chronostd::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);

// 容易和 const_cast/ reinterpret_cast 混淆

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
// C++26 特性(预计)
#include <embed>

constexpr auto shader_code = std::embed("shader.frag");
static_assert(shader_code.size() > 0);

C++23 的实用工具主要还是 std::to_underlyingstd::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()) { /* ... */ }

// C++23
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()) { /* ... */ }

// C++23
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")) { /* ✅ 替代 find() != npos */ }

// 检查前缀/后缀 (C++20 已有 starts_with/ends_with)
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> // C++23
#include <format> // C++20
#include <map>
#include <vector>
#include <string_view>

enum Color : int { Red = 1, Green, Blue };

int main() {
// std::print (C++23)
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::to_underlying (C++23)
Color c = Color::Red;
int underlying = static_cast<int>(c); // 旧方式
// int underlying = std::to_underlying(c); // C++23 方式
(void)underlying;

// std::map/vector::contains (C++23)
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::contains (C++23)
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::expectedco_await 那样重磅,但它们直接提升了日常编码体验。std::print 让输出代码从 3 行变 1 行,contains() 让布尔表达式更符合自然语言习惯,to_underlying 让类型转换意图更清晰。这些改进值得你在下一个项目中也用起来。


📚 C++23 新特性 系列导航

本文是《C++23 新特性》系列第 3/4 篇。

方向章节
◀ 上一篇(二)if consteval 与 Deducing this
下一篇 ▶(四)Ranges 增强
📖 全部 4 篇目录(点击展开)
  1. (一)std::expected
  2. (二)if consteval 与 Deducing this
  3. (三)std::print / to_underlying ← 当前
  4. (四)Ranges 增强