C++23 引入了一项新特性:显式 this
参数(也称为 “deducing this
”),它允许将成员函数的 this
指针作为显式参数传递,从而带来更多灵活性和新特性。
基本语法
传统成员函数:
struct S {
void foo() { /* this 是隐式的 */ }
};
显式 this
参数语法:
struct S {
void foo(this S& self) { /* self 替代了 this */ }
};
主要特点和优势
- 统一成员函数和自由函数的调用语法
- 支持值语义和引用语义的显式选择
- 支持完美转发
- 简化 CRTP 模式
- 支持静态成员函数中的对象访问
使用示例
1. 基本使用
struct Person {
std::string name;
// 传统方式
void print_impl() {
std::cout << name << "\n";
}
// 显式 this 参数方式
void print(this Person& self) {
std::cout << self.name << "\n";
}
};
int main() {
Person p{"Alice"};
p.print(); // 两种方式调用相同
}
2. 值语义 vs 引用语义
struct Value {
void by_ref(this Value& self) {
std::cout << "by reference\n";
}
void by_value(this Value self) {
std::cout << "by value\n";
}
};
int main() {
Value v;
v.by_ref(); // by reference
v.by_value(); // by value
Value{}.by_ref(); // 错误:不能绑定右值到非const左值引用
Value{}.by_value(); // OK: by value
}
3. 完美转发
struct Forwarder {
template <typename Self>
void forward(this Self&& self) {
std::cout << "forwarding...\n";
other_function(std::forward<Self>(self));
}
};
4. 简化 CRTP 模式
传统 CRTP:
template <typename Derived>
struct Base {
void interface() {
static_cast<Derived*>(this)->implementation();
}
};
使用显式 this
:
template <typename Derived>
struct Base {
void interface(this Derived& self) {
self.implementation();
}
};
5. 静态成员函数中的对象访问
struct StaticAccess {
static void print(this const StaticAccess& self) {
std::cout << "Accessing from static-like function\n";
}
};
与 Lambda 表达式的结合
显式 this
参数也可以与 lambda 表达式结合使用:
auto make_counter = []() {
return [](this auto& self) -> int {
static int count = 0;
return ++count;
};
};
int main() {
auto counter = make_counter();
std::cout << counter() << "\n"; // 1
std::cout << counter() << "\n"; // 2
}
注意事项
- 显式
this
参数必须是第一个参数 - 只能有一个显式
this
参数 - 类型可以是值类型、引用类型或转发引用
-
- 参数前使用this关键字。
- 参数通常命名self,可任意命名。
- 如果使用了显式对象参数,就不会有隐式this指针;在定义显式对象参数的方法内,只能通过显式方式引用成员变量,如下面例子中self.name(如果直接用name会报错)
- 是否定义显式对象参数,对方法调用没有任何影响,只是便于开发实现。参见下面的print()方法实现和调用。
定义时有显式参数,调用时编译器会自动传入类对象,不用手动传入对象作为参数。 - 对于 const 成员函数,需要将
this
参数声明为 const 引用 - 目前需要支持 C++23 的编译器(如 GCC 13+、MSVC 19.30+)
用途:
实际应用场景
- 创建更灵活的接口:可以根据需要选择值语义或引用语义
- 改进模板设计:简化基于模板的设计模式
- 更好的性能控制:明确选择复制或引用行为
- 统一自由函数和成员函数:使两者可以更容易地互相转换
- 对于&和&&重载的方法,使用显式对象参数更易读。Professional c++ 6e,p327
- 类方法模板中,使用显式对象参数+转发引用,一个方法同时处理Object&, const Object&, Object&&多种类型,避免了方法重载。Professional c++ 6e,p473
- 简化lambda递归
这项特性为 C++ 带来了更一致的面向对象编程体验,同时保持了语言的高效性和灵活性。