25、【C++】C++23常用特性
25、【C++】C++23常用特性
目录
一、核心语言特性
1.1 deducing this
1.1.1 简化CRTP模式
CRTP(Curiously Recurring Template Pattern)是一种常用设计模式,传统实现需要手动传递派生类类型:
// C++20及之前
template <typename Derived>
struct Base {
void func() {
static_cast<Derived*>(this)->impl(); // 手动转换this
}
};
struct Derived : Base<Derived> {
void impl() { /* 实现 */ }
};
C++23通过deducing this自动推导派生类类型:
// C++23
struct Base {
// this作为模板参数,自动推导Self为Derived
template <typename Self>
void func(this Self&& self) {
self.impl(); // 直接调用派生类impl,无需static_cast
}
};
struct Derived : Base {
void impl() { std::cout << "Derived::impl" << std::endl; }
};
1.1.2 成员函数模板推导
deducing this允许成员函数根据this的类型(左值/右值)推导模板参数:
struct MyClass {
// 根据this是左值还是右值,推导Self的类型
template <typename Self>
auto get(this Self&& self) {
if constexpr (std::is_lvalue_reference_v<Self>) {
return std::ref(self.data); // 左值返回引用
} else {
return std::move(self.data); // 右值返回移动后的值
}
}
private:
std::string data = "hello";
};
1.2 constexpr增强
1.2.1 constexpr for循环
C++23允许for循环在constexpr函数中使用,支持编译期迭代:
constexpr auto sum(std::initializer_list<int> list) {
int s = 0;
for (int x : list) { // constexpr for循环
s += x;
}
return s;
}
constexpr int s = sum({1, 2, 3}); // 编译期计算s=6
1.2.2 constexpr动态内存管理优化
C++23放松constexpr中动态内存的限制,允许new/delete在编译期更灵活使用:
constexpr std::vector<int> create_vector() {
std::vector<int> v;
v.reserve(3);
v.push_back(1);
v.push_back(2);
v.push_back(3);
return v;
}
constexpr auto vec = create_vector(); // 编译期创建vector
1.3 if consteval
1.3.1 编译期与运行期分支
if consteval判断当前上下文是否为编译期常量表达式,选择执行分支:
consteval int compile_time() { return 42; }
int run_time() { return 42; }
auto func(bool flag) {
if consteval {
return compile_time(); // 编译期上下文执行
} else {
return run_time(); // 运行期上下文执行
}
}
int main() {
int a = func(true); // 调用compile_time()(编译期)
int b = func(false); // 调用run_time()(运行期)
return 0;
}
1.3.2 与constexpr if的区别
if consteval:判断上下文是否为编译期(如consteval函数内),分支在编译期确定。constexpr if:判断条件是否为编译期常量,分支在编译期静态选择(未选中分支不生成代码)。
1.4 其他语言特性
1.4.1 static operator[]
静态下标运算符允许通过类名直接访问静态成员:
struct Config {
static std::unordered_map<std::string, int> settings;
static int operator[](const std::string& key) {
return settings[key];
}
};
std::unordered_map<std::string, int> Config::settings = {{"max_size", 100}};
int main() {
int max_size = Config["max_size"]; // 直接通过类名调用静态operator[]
return 0;
}
1.4.2 throw() noexcept简化
throw()等价于noexcept(true),简化异常规范:
void func() throw() {} // 等价于void func() noexcept(true)
void func2() noexcept {} // C++11 noexcept,等价于noexcept(true)
1.4.3 [[assume]]属性
[[assume(condition)]]提示编译器假设condition为真,优化代码(如删除冗余检查):
int div(int a, int b) {
[[assume(b != 0)]]; // 假设b不为0,编译器可删除除0检查
return a / b;
}
二、标准库新增特性
2.1 std::expected
2.1.1 结果与错误的统一表示
std::expected<T, E>表示操作结果(T)或错误(E),替代std::optional+错误码:
#include <expected>
#include <string>
enum class Error { InvalidInput, OutOfRange };
std::expected<int, Error> parse_int(const std::string& s) {
if (s.empty()) return std::unexpected(Error::InvalidInput);
// ... 解析逻辑 ...
return 42;
}
int main() {
auto res = parse_int("42");
if (res.has_value()) {
int value = res.value(); // 获取结果
} else {
Error err = res.error(); // 获取错误
}
return 0;
}
2.1.2 and_then/or_else链式调用
std::expected支持链式调用,处理成功或失败的后续逻辑:
// 成功时调用func1,失败时调用func2
auto res = parse_int("42")
.and_then([](int x) { return x * 2; }) // 成功:42 → 84
.or_else([](Error e) { return std::unexpected(e); }); // 失败:传递错误
2.2 扁平化容器(flat_map/flat_set)
2.2.1 内部结构与性能对比
std::flat_map/std::flat_set内部用std::vector存储键值对,排序后通过二分查找访问:
- 优点:内存连续,缓存友好,遍历速度快于
std::map。 - 缺点:插入/删除需要移动元素,性能较差。
#include <flat_map>
#include <map>
#include <chrono>
int main() {
std::flat_map<int, int> fm;
std::map<int, int> m;
// 插入性能:map更快(O(log n) vs flat_map O(n))
// 遍历性能:flat_map更快(缓存局部性)
return 0;
}
2.2.2 适用场景
- 遍历密集型场景:如日志分析、统计报表。
- 内存受限环境:连续存储减少内存碎片。
2.3 std::print/std::println
2.3.1 格式化输出到标准流
std::print/std::println直接格式化输出到stdout,简化打印代码:
#include <print>
#include <string>
int main() {
std::print("Hello, {}!\n", "World"); // 格式化输出(无自动换行)
std::println("Hello, {}!", "World"); // 自动添加换行
return 0;
}
2.3.2 与std::format的配合
std::print内部使用std::format的格式化逻辑,支持相同的格式规范:
std::println("PI: {:.2f}", 3.14159); // 输出:PI: 3.14
2.4 ranges::to
2.4.3 范围转换为容器
ranges::to<C>将范围转换为容器C,简化范围到容器的转换:
#include <ranges>
#include <vector>
#include <views>
int main() {
auto view = std::views::iota(1, 6); // 生成1-5
std::vector<int> vec = view | std::ranges::to<std::vector>(); // 转换为vector
return 0;
}
2.5 其他标准库特性
2.5.1 std::expected
(见2.1节)
2.5.2 std::byteswap
字节序转换函数,支持整数类型的大小端转换:
#include <bit>
int main() {
uint16_t x = 0x1234;
uint16_t y = std::byteswap(x); // 0x3412(小端→大端)
return 0;
}
2.5.3 std::stacktrace
获取堆栈跟踪信息,辅助错误调试:
#include <stacktrace>
#include <iostream>
void func() {
auto st = std::stacktrace::current(); // 获取当前堆栈
std::cout << st << std::endl; // 打印堆栈信息
}
int main() {
func();
return 0;
}
三、C++23特性应用场景
3.1 deducing this简化多态设计
使用deducing this实现静态多态,避免CRTP的繁琐类型传递:
struct Drawable {
template <typename Self>
void draw(this Self&& self) {
self.draw_impl(); // 调用派生类实现
}
};
struct Circle : Drawable {
void draw_impl() { std::cout << "Circle" << std::endl; }
};
int main() {
Circle c;
c.draw(); // 调用Drawable::draw → Circle::draw_impl
return 0;
}
3.2 std::expected统一错误处理
在网络请求中返回结果或错误:
std::expected<Response, NetworkError> fetch_data(const std::string& url) {
// ... 网络请求逻辑 ...
if (success) return Response{...};
else return std::unexpected(NetworkError::Timeout);
}
3.3 flat_map优化遍历性能
游戏实体组件系统中,使用flat_map存储组件,加快遍历速度:
std::flat_map<EntityID, TransformComponent> transforms;
std::flat_map<EntityID, RenderComponent> renders;
// 遍历所有实体组件,flat_map缓存友好
for (auto& [id, transform] : transforms) {
auto it = renders.find(id);
if (it != renders.end()) {
render(transform, it->second);
}
}
四、编译器支持与迁移
4.1 主流编译器支持情况
- GCC:13+支持部分C++23特性(如
std::expected、flat_map)。 - Clang:16+支持部分特性。
- MSVC:Visual Studio 2022 17.5+支持部分特性。
4.2 从C++20迁移到C++23
- 渐进式迁移:优先使用稳定特性(如
std::print、flat_map)。 - 兼容性:C++23特性大多向下兼容,可混合使用C++20代码。
- 工具链更新:确保编译器版本支持所需特性,启用
-std=c++23编译选项。
以上内容,全面覆盖了C++23的核心语言特性和标准库新增功能。C++23聚焦于提升开发效率和性能,完善C++20引入的现代特性,是C++语言持续进化的重要一步。合理应用这些特性可使代码更简洁、高效、易维护。
更多推荐

所有评论(0)