跳转至

💡 一分钟速览:通过已编译验证的递归 lambda 与成员函数示例解释 C++23 deducing this、const 和值类别转发,以及编译器要求和常见陷阱。

最后更新:2026-10-31

C++23 deducing this:显式对象参数

*显式对象参数*让成员函数或 lambda 给调用它的对象命名。常说的“deducing this”通常指该参数类型由编译器推导的写法。这是 WG21 P0847R7 引入的 C++23 语言特性,不属于 C++20 lambda。

当一个操作要覆盖 const 和不同值类别,或递归 lambda 需要调用自身时,它很有用。普通成员函数若没有这些需求,传统的隐式 this 更容易读懂。

不用 std::function 写递归 lambda

#include <cassert>

int main() {
    auto factorial = [](this auto const& self, int n) -> int {
        return n <= 1 ? 1 : n * self(n - 1);
    };
    assert(factorial(5) == 120);
}

第一个参数指向 lambda 对象,self(n - 1) 再次调用它。调用方仍写 factorial(5),不必把 lambda 自身显式传进去,也无需 std::function 类型擦除包装。这并不能证明所有递归 lambda 都更快;性能敏感时应检查生成代码并测试真实负载。

一个成员函数保留 const 与值类别

#include <string>
#include <type_traits>
#include <utility>

struct Buffer {
    std::string text;

    template<class Self>
    decltype(auto) get(this Self&& self) {
        return (std::forward<Self>(self).text);
    }
};

static_assert(std::is_same_v<decltype(std::declval<Buffer&>().get()), std::string&>);
static_assert(std::is_same_v<decltype(std::declval<const Buffer&>().get()), const std::string&>);
static_assert(std::is_same_v<decltype(std::declval<Buffer&&>().get()), std::string&&>);

int main() {
    Buffer b{"hello"};
    return b.get() == "hello" ? 0 : 1;
}

Self&& 根据调用方推导;std::forward<Self>(self) 保留 const 与左值/右值类别。return (expression); 的括号不能随意去掉:配合 decltype(auto),括号保留表达式的引用类型;去掉后,这种成员访问可能推导出成员的声明类型并返回副本。不要在临时 Buffer 已销毁后继续保存从它取得的引用。

编译器与设计边界

使用 Clang 18 或更新版本,以 clang++ -std=c++23 -Wall -Wextra -pedantic example.cpp 编译。本文两个示例已在 Compiler Explorer 的 Clang 20.1.0 中编译并运行。本站本地的 Apple Clang 16 尚不支持该特性,仅加 -std=c++23 不够;复制代码前请查看 Clang 实现状态或所用编译器的支持情况。

显式对象成员函数内部用命名参数 self,不能依赖隐式 this。若按值传对象会产生复制;按所有权与修改需求选择 Self&&、const Self& 或具体引用类型。显式对象成员函数不能是 virtual。

接着读 const 全家桶了解修饰符和转发,再读 C++20 lambda了解 C++23 以前的背景。这两篇只含简短示例,本文集中解释功能的设计与边界。

参考资料

评论