C++23 的显式 this 参数(explicit object parameter)

发布于:2025-06-29 ⋅ 阅读:(13) ⋅ 点赞:(0)

C++23 引入了一项新特性:显式 this 参数(也称为 “deducing this”),它允许将成员函数的 this 指针作为显式参数传递,从而带来更多灵活性和新特性。

基本语法

传统成员函数:

struct S {
    void foo() { /* this 是隐式的 */ }
};

显式 this 参数语法:

struct S {
    void foo(this S& self) { /* self 替代了 this */ }
};

主要特点和优势

  1. 统一成员函数和自由函数的调用语法
  2. 支持值语义和引用语义的显式选择
  3. 支持完美转发
  4. 简化 CRTP 模式
  5. 支持静态成员函数中的对象访问

使用示例

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
}

注意事项

  1. 显式 this 参数必须是第一个参数
  2. 只能有一个显式 this 参数
  3. 类型可以是值类型、引用类型或转发引用
    1. 参数前使用this关键字。
  4. 参数通常命名self,可任意命名。
  5. 如果使用了显式对象参数,就不会有隐式this指针;在定义显式对象参数的方法内,只能通过显式方式引用成员变量,如下面例子中self.name(如果直接用name会报错)
  6. 是否定义显式对象参数,对方法调用没有任何影响,只是便于开发实现。参见下面的print()方法实现和调用。
    定义时有显式参数,调用时编译器会自动传入类对象,不用手动传入对象作为参数。
  7. 对于 const 成员函数,需要将 this 参数声明为 const 引用
  8. 目前需要支持 C++23 的编译器(如 GCC 13+、MSVC 19.30+)

用途:

实际应用场景

  1. 创建更灵活的接口:可以根据需要选择值语义或引用语义
  2. 改进模板设计:简化基于模板的设计模式
  3. 更好的性能控制:明确选择复制或引用行为
  4. 统一自由函数和成员函数:使两者可以更容易地互相转换
  5. 对于&和&&重载的方法,使用显式对象参数更易读。Professional c++ 6e,p327
  6. 类方法模板中,使用显式对象参数+转发引用,一个方法同时处理Object&, const Object&, Object&&多种类型,避免了方法重载。Professional c++ 6e,p473
  7. 简化lambda递归

这项特性为 C++ 带来了更一致的面向对象编程体验,同时保持了语言的高效性和灵活性。