文章目录
- [第一章 C++23语言特性](#第一章 C++23语言特性)
-
- [1.1 Deducing this](#1.1 Deducing this)
-
- [1.1.1 Decucing this 语法格式](#1.1.1 Decucing this 语法格式)
- [1.1.2 应用场景](#1.1.2 应用场景)
- [1.1.3 总结](#1.1.3 总结)
本文讲解C++23新特性之Deducing this.
第一章 C++23语言特性
1.1 Deducing this
在C++20之前,一个类的this指针隐士藏在这个类的成员函数的第一个参数中。在类中提供一个获取属性的方法get(),为了支持左值,const左值,右值等不同场景,实现如下:
cpp
struct Container {
std::string val;
// 1. 可变左值
std::string& get()& { return val; }
/*编译器视角下的this
std::string& get(Container* const this)
{
return this->val;
}
*/
// 2. const 左值
const std::string& get() const& { return val; }
/*编译器视角下的this
std::string& get(const Container* const this)
{
return this->val;
}
*/
// 3. 右值 (移动语义)
std::string&& get()&& { return std::move(val); }
/*
std::string&& get(Container* const this)
{
// 这里的 val 实际上等价于 static_cast<std::string&&>(this->val)
return std::move(this->val);
}
*/
};
void test() {
Container c;
c.get() = "World"; // 调用 std::string& get()& -> 返回 string&
std::cout << c.get() << endl; // 输出 World
const Container cc;
std::cout << cc.get() << endl;; // 调用 const std::string& get()
std::string s = Container{}.get(); //
std::cout << Container{}.get() << endl; //调用 std::string&& get()&&
}
在编译器看来,这3个get()也是函数重载,因为每一个get()都隐藏了this指针,编译器根据不同的this指针生成3个函数符号,调用时,从3个符号中选择最优的一个调用。
上边写法的写法实际上有些重复,有没有更好的实现方式呢?
在C++23之前的奇异递归模板模式,为了在基类中使用派生类的方法,不得不使用CRTP,这需要static_cast转换,这个转换实际上一个this指针调整。这种CRTP方式,代码还是比较晦涩难懂,涉及到继承,this指针转换等。CRTP请看这个博客:
https://blog.csdn.net/weixin_43916755/article/details/155103708?sharetype=blogdetail&sharerId=155103708&sharerefer=PC&sharesource=weixin_43916755&spm=1011.2480.3001.8118
C++23为了解决this问题,允许显示声明对象参数,通过万能引用模板推导参数的类型,解决重写书写问题。
1.1.1 Decucing this 语法格式
在成员函数的第一个参数前加上 this 关键字。
在成员函数中,第一个参数加上 this.
cpp
struct X
{
// 传统写法
void foo()
{
// this 指针隐式转换为 X*
}
// C++23 写法
void bar(this X& self) //
{
// self是一个名字,类型是 X&
}
// 最强模板写法
template<typename T>
void baz(this T&& self)
{
// self 可以是任何类型的引用
}
};
1.1.2 应用场景
示例1:改进get
将上面3个get使用Deducing this合并为1个。
cpp
struct Container2 {
std::string val = "Hello";
// 一个函数统治所有
template <typename Self>
auto&& get(this Self&& self) {
// 转发 self (即转发 *this)
// 如果 self 是 const&,返回 const&
// 如果 self 是 &&,返回 && (移动)
return std::forward<Self>(self).val;
}
};
void test()
{
Container2 c;
c.get() = "World"; // 调用 get(),返回 string&
cout << c.get() << endl; // 输出 World
const Container2 cc;
cout << cc.get() << endl; // 调用 get(),返回 const string&
std::string s = Container2{}.get(); // 调用 get(),返回 string&&
cout << Container2{}.get() << endl; // 输出 Hello
}
示例2:递归Lambda
在 C++23 之前,Lambda 表达式很难直接递归调用自己(因为在 Lambda 内部无法引用自身类型)。通常需要借助 std::function(有性能开销)或复杂的 Y 组合子。
现在通过Deducing this,Lambda可以显示捕获自己。
cpp
void test()
{
auto fib = [](this auto&& self,int n)
{
if (n < 2)
{
return n;
}
return self(n - 1) + self(n - 2);
};
std::cout << "Fibonacci(10): " << fib(10) << std::endl;
// Fibonacci(10): 55
}
示例3:实现简化版的CRTP
复杂版的CRTP如下:
cpp
template<typename T>
class Base
{
public:
// 在基类中调用派生类的方法
void handleDerived()
{
T& derived = static_cast<T&>(*this);// 派生类对象也是基类对象,静态转换
derived.myfunc();
}
private:
Base() {}
friend T; // 友元
};
class Derived1 : public Base<Derived1> // 普通类
{
public:
void myfunc()
{
cout << "Derived1::myfunc()执行了" << endl;
}
};
template <typename T>
class Derived2 : public Base<Derived2<T>>
{
public:
void myfunc()
{
cout << "Derived2::myfunc()执行了" << endl;
}
};
class Derived3 : public Base<Derived3>
{
// 未实现 myfunc(),尝试调用会导致编译错误
};
void test()
{
Derived1 myde;
myde.handleDerived();
// 输出:Derived1::myfunc()执行了
Derived2<int> myde2;
myde2.handleDerived();
// Derived2::myfunc()执行了
Derived3 myde3;
//myde3.handleDerived();
// 编译错误:Derived3 未实现 myfunc()
}
具体讲解,见上面博客链接。
简化版的如下,可以看到避免了T 类型的static_cast转换。
cpp
class Base
{
public:
template<typename Self>
void handleDerived(this Self&& self)
{
self.myfunc();
}
};
class Derived1 : public Base
{
public:
void myfunc()
{
std::cout << "Derived::myfunc() called" << std::endl;
}
};
class Derived2 : public Base
{
public:
void myfunc()
{
std::cout << "Derived2::myfunc() called" << std::endl;
}
};
void test()
{
Derived1 d;
d.handleDerived(); // 调用基类模板方法
// 输出: Derived::myfunc() called
Derived2 d2;
d2.handleDerived(); // 调用基类模板方法
// 输出: Derived2::myfunc() called
}
1.1.3 总结
Deducing this 是C++这门古老语言迈向现代化的又一重要步伐。主要特性总结如下:
1 去重:它用一个模板函数替代了 const/non-const/lvalue/rvalue 的四个重载。
2 解耦:它让 CRTP 模式不再依赖复杂的模板继承语法,实现了真正的"鸭子类型"混入。
3 赋能:它让 Lambda 递归变得轻而易举。