c++23的Deducing this是什么,如何改变类成员函数的设计? (显式对象参数)

de

ducing this是C++23引入的语法,将成员函数隐式this参数显式化为带类型占位符的形参,支持对cv/ref限定符进行模板推导;基本形式为在参数列表首位写this auto& self等。

什么是 deducing this(显式对象参数)

这是 C++23 引入的语法,允许把成员函数的第一个隐式 this 参数写成显式的、带类型占位符的形参,从而让编译器能对 this 的 cv/ref 限定(如 const&&)做模板参数推导。它不是“重载 this”,而是把原本隐式绑定的对象,变成可参与模板推导的普通形参。

怎么写显式对象参数的成员函数

基本形式是:在函数声明中把 this 拿出来,写在参数列表最前面,类型用 auto& 或更精确的 MyClass& 等,支持 constvolatile、引用限定符;若用 auto,就触发推导。

struct Vec {
    int x = 0;

    // 显式对象参数 + auto → deducing this
    void print(this auto& self) {
        std::cout << "x = " << self.x << "\n";
    }

    // 也可限定 const
    int get(this const auto& self) const { return self.x; }

    // 支持右值限定(注意:this 后面的 && 是函数的引用限定符,不是参数的)
    void consume(this Vec&& self) { x = -1; }
};
  • this auto& 表示“接受任意 cv/ref 限定的 Vec 对象,并推导其完整类型”
  • 不能写 this auto self(必须带引用,因为 this 总是引用语义)
  • 显式对象参数必须是第一个参数,且只能有一个
  • 仍可有其他普通参数,例如 void set(this auto& self, int v)

它如何改变成员函数重载与设计习惯

以前靠 const/non-const 成员函数重载区分读写权限,现在可以用一个 auto& 函数覆盖多种情况,再用 if constexpr 分支处理逻辑差异——尤其适合通用工具类或需要精细控制 cv/ref 行为的场景。

struct Box {
    std::string data;

    // 替代 const 和 non-const 两个版本
    std::string& get(this auto& self) {
        if constexpr (std::is_const_v) {
            return const_cast(self.data);
        } else {
            return self.data;
        }
    }
};
  • 旧写法要定义 const std::string& get() conststd::string& get() 两份
  • 新写法只需一份,但要注意:返回非常量引用给 const 对象需显式 const_cast,否则编译失败
  • 无法替代所有重载:比如想对 Box&& 做移动优化,仍需单独写右值引用限定函数(this Box&&),因为 auto& 不会匹配纯右值
  • 显式对象参数函数不参与 ADL(除非显式指定作用域),这点和普通成员函数一致

容易踩的坑和兼容性注意点

这不是“语法糖”,它的行为和传统成员函数有实质差异:对象参数参与模板实例化,可能造成意外的函数模板膨胀,也可能因推导出太具体的类型而失去多态性。

  • 不能在虚函数中使用 this auto& —— 虚函数要求签名完全一致,而 auto 推导后每个调用点生成不同函数,无法构成虚函数重写
  • 类模板中使用时,this auto& 推导的是具体特化类型(如 Vec&),不是模板参数本身,别误以为能直接拿到 T
  • Clang 15+、GCC 13+、MSVC 19.34+ 支持;C++20 模式下不识别,必须启用 -std=c++23 或等效开关
  • 调试时栈帧里会显示显式参数名(如 self),而不是隐式的 this,部分调试器可能尚未完全适配显示

真正关键的转变在于:你开始把“调用者对象”当作一个可推导、可约束、可 if constexpr 分支的输入,而不是语言强加的隐式上下文。这在写泛型容器适配器或 fluent 接口时特别有用,但也意味着你得更小心地控制推导边界——毕竟,auto 不会自动理解你的设计意图。