【C++23】(二)if consteval 与 Deducing this:更多编译期能力
C++23 让”编译期计算”和”对象方法调用”都变得更强大。
if consteval让你在编译期分支判断,而deducing this则彻底改变了成员函数的设计方式——不再需要模板就能实现”泛型方法”。
一、if consteval:编译期还是运行期?
痛点回顾
在 C++20 引入 consteval 之后,我们可以用它声明”必须在编译期求值”的函数:
1 | consteval int square(int n) { return n * n; } |
问题来了:如果你有一个函数,想让它在”编译期返回 42,运行时返回 0”——在 C++23 之前,你无法判断当前是否处于常量求值上下文。
if consteval 的解决方案
1 | // C++23 |
if consteval 是专门用于常量求值上下文的 if 语句,它的条件部分没有括号——这是 C++23 的新语法。
实际应用:编译期vs运行期差异化实现
1 | consteval auto process(int n) { |
二、Deducing this:成员函数的革命
传统方式的局限性
1 | struct Widget { |
问题:
print()只能被Widget调用,无法泛化到其他有value成员的类- 菱形继承中,
this指针调整容易出错 - 实现 CRTP 等模式时语法繁琐
显式对象参数语法
C++23 允许你用 this 作为显式对象参数(explicit object parameter):
1 | struct Widget { |
语义:调用 w.print() 时,self 会绑定到 w。这不是模板,却实现了”方法可被其他类型复用”的能力。
模板版本:真正的泛型方法
1 | struct Widget { |
这就是 deducing this 的魔力——一个非模板函数,却能自动推导调用者的类型。
三、完整示例
1 |
|
输出:
1 | Widget: 100 |
四、Deducing this 的应用场景
场景 1:简化实现继承中的方法转发
1 | struct Base { |
场景 2:解决菱形继承问题
1 | struct A { int x; }; |
场景 3:泛型方法的简洁实现
1 | struct Data { |
五、if consteval vs __builtin_is_constant_evaluating
| 特性 | if consteval | __builtin_is_constant_evaluting |
|---|---|---|
| 标准 | C++23 标准 | GCC/Clang 扩展 |
| 可移植性 | ✅ 通用 | ❌ 仅 GCC/Clang |
| 语法 | if consteval { } | if (__builtin_is_constant_evaluting()) |
| 上下文判断 | 精确 | 近似 |
建议:始终使用 if consteval,它是标准写法,语义也更清晰。
总结:
if consteval和deducing this是 C++23 最重要的两大手册(handbook)改进。前者让编译期分支判断成为标准语法,后者让成员函数摆脱了”只能被单一类型调用”的束缚。如果你写过模板元编程或处理过复杂的继承关系,你会立即爱上这两个特性。
📚 C++23 新特性 系列导航
本文是《C++23 新特性》系列第 2/4 篇。
| 方向 | 章节 |
|---|---|
| ◀ 上一篇 | (一)std::expected |
| 下一篇 ▶ | (三)std::print / to_underlying |
📖 全部 4 篇目录(点击展开)
语法/控制流可视化
下面是一张马卡龙色 Mermaid 图,帮你从图形角度把握本章涉及的语法与控制流。
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#FFE5EC', 'primaryTextColor': '#5D5D5D', 'primaryBorderColor': '#FFB3C6', 'lineColor': '#B5EAD7', 'secondaryColor': '#C7CEEA', 'tertiaryColor': '#FFDAC1'}}}%%
flowchart TD
CALL["📞 foo(x)"] --> DT{"🪄 deducing this<br/>推导 Self"}
DT --> S1["Self = Foo"]
DT --> S2["Self = const Foo"]
DT --> S3["Self = Foo&"]
S1 --> BODY["🧩 进入函数体<br/>self.member"]
S2 --> BODY
S3 --> BODY
BODY --> CV{"🤔 if consteval?"}
CV -- "编译期调用" --> CE["⚙️ 编译期分支<br/>编译期求值"]
CV -- "运行期调用" --> RE["🏃 运行期分支<br/>运行时执行"]
style CALL fill:#FFE5EC,stroke:#FFB3C6,color:#5D5D5D
style DT fill:#FFDAC1,stroke:#FFB3C6,color:#5D5D5D
style S1 fill:#C7CEEA,stroke:#A8DADC,color:#5D5D5D
style S2 fill:#C7CEEA,stroke:#A8DADC,color:#5D5D5D
style S3 fill:#C7CEEA,stroke:#A8DADC,color:#5D5D5D
style BODY fill:#B5EAD7,stroke:#A8DADC,color:#5D5D5D
style CV fill:#FFDAC1,stroke:#FFB3C6,color:#5D5D5D
style CE fill:#FFE5EC,stroke:#FFB3C6,color:#5D5D5D
style RE fill:#C7CEEA,stroke:#A8DADC,color:#5D5D5D本章讲 C++23 的 if consteval 与 deducing this,对比维度:本特性 vs C++17/20 旧写法 vs 其他语言对应特性。
对比分析
一、本特性 vs 旧写法
| 维度 | C++23 写法 | C++20 写法 | 影响 |
|---|---|---|---|
| 编译期函数与运行期分支 | if consteval { ... } else { ... } | 需要用 std::is_constant_evaluated() | 写法更直白 |
| 泛型成员函数 | void foo(this Self& self) {...} | 需要 CRTP / std::enable_if | 不再需要模板也能泛型 |
| 与 lambda 结合 | deducing this lambda | 模板 lambda | 统一 |
二、对比其他语言
| 语言 | 编译期与运行期分支 | 泛型方法 | 备注 |
|---|---|---|---|
| C++23 | if consteval | deducing this | 显式 |
| Rust | const fn + 调用点分析 | trait + Self | 不同思路 |
| Java | ❌ | bounded generics | 受限 |
| Python | ❌ | duck typing | 运行时 |
| C# | ❌ | generic methods | 较受限 |
三、优缺点
优点:
if consteval终于比is_constant_evaluated()更优雅- deducing this 把”成员函数当模板”标准化
缺点:
- 编译器支持普及度仍需观察
- 与既有 CRTP 代码如何迁移需谨慎
四、何时选
- 写库代码:直接用 deducing this 替代 CRTP
- 工具函数:if consteval 让意图明确