C++23’s New Feature: Deducing this
0. 引言
C++23带来了新的语法支持:Deducing this,作为C++23中的重要特性,使C++程序的编写更加简洁和灵活,但也带入了一定的理解成本。本篇文章旨在介绍该特性的语法、基本使用与一些使用场景。
本文中所有示例代码的编译及运行环境:Windows, clang+llvm-18.1.8-pc-x86_64-windows-msvc
编译器及参数:clang++ -std=c++23
1. 简单了解Deducing this
1.1 Deducing this解决了什么问题?
In C++03, member functions could have cv-qualifications, so it was possible to have scenarios where a particular class would want both a
constand non-constoverload of a particular member. (Note that it was also possible to wantvolatileoverloads, but those are less common and thus are not examined here.) In these cases, both overloads do the same thing — the only difference is in the types being accessed and used. This was handled by either duplicating the function while adjusting types and qualifications as necessary, or having one overload delegate to the other.In C++11, member functions acquired a new axis to specialize on: ref-qualifiers. Now, instead of potentially needing two overloads of a single member function, we might need four:
&,const&,&&, orconst&&We believe that the ability to write cv-ref qualifier-aware member function templates without duplication will improve code maintainability, decrease the likelihood of bugs, and make fast, correct code easier to write.
我们可以看出,在(C++23)之前,很多时候为了应对不同cv-ref qualified的对象,我们需要对类成员函数做出&, const&, &&和const&&的重载,然而,这些所有的重载函数都在执行同一份任务,毫无疑问,这是一种形式的冗余。而Deducing this正是为解决这一问题而生。
1.2 Deducing this的如何解决上述问题?
在C++23之前,一个类成员函数的代码可能如下:
class A {
public:
void foo() & {
// do something
}
void foo() const& {
// do the same thing
}
void foo() && {
// do the same thing
}
void foo() const&& {
// do the same thing
}
};
在C++23之后,它们可以被统一为一个函数:
class A {
public:
template<typename Self>
void foo(this Self&& self) {
// do something
}
};
这便是Deducing this的基本写法,self称为显式对象形参,其语法为:
ret-type member-function-name(this type-name param-name)
它说明了我们将以何种类型来操作对象,且在名字查找中,void A::foo() const&与void A::foo(this A const& self)等价,并以此类推。
同样地,正是因为当前对象作为形参,使得当前的对象类型可以利用模板(this auto&&也可行)进行类型推导,也就实现了Deducing this,统一了四种函数。
注意:显示对象形参只能作为非虚(见下文2.6说明)的,非静态的成员函数的首个形参。
void foo(this auto& self) {} // correct
void foo(int a, this auto& self) {} // error
static void foo(this auto& self) {} // error
virtual void foo(this auto& self) {} // error
2. 关于Deducing this的语法细节
2.1 如果要对不同的成员函数做出差异化处理怎么办?
有两种方法,第一种是进行函数重载,使用cv-ref修饰函数或使用cv-ref修饰显式对象形参类型;第二种是写出函数模板特化。
注意:void A::foo(this A&)在语义上类似static函数,无法与static void A::foo()进行重载(注:
A member function with an explicit object parameter is a third kind of member function that’s sort of halfway in between those two(all member functions are either static member functions or non-static member functions). They’re like semi-static member functions.
P0847R7.Not quite static, not quite non-static)。
另外,(重载决议的规则)如果:
- 显式对象形参声明为
this auto类型,那么当成员函数中出现其它显式对象形参类型的重载时:- 如果重载类型为
&&类型,被右值调用时,优先考虑具有this auto类型的显显式对象形参的函数; - 如果重载类型为
&类型,被左值调用时,优先考虑该重载。
- 如果重载类型为
- 显式对象形参声明为模板的非引用类型,而其它重载函数是该模板的特化时:
- 如果模板特化为
&&或&类型,被相应引用类型调用时,优先考虑非模板特化函数。
- 如果模板特化为
- 如果存在按值类型传递对象的重载,始终优先考虑这一重载,且同时如果:
- 定义了
this <class-type> cv-qulifier&或者this <class-type> cv-qulifier&&的重载函数,编译可能通过,但一定会产生模糊调用。
- 定义了
