双重派发 双重委派 双派发 double dispatch
参考自《c++高级编程》罗能著
解决的问题:
在多态时,初始化需要明确接口的实际类型:
Animal animal= bear();
animal.move(); //注意这里是实例化后,访问这个基类的方法。是c++的多态
这个animal指针指向真正的实例化类,调用的是animal接口里的方法。编译时可以通过。
这个是运行时多态!!!
如果确定类型时需要两个,甚至多个类型确认,就会有问题。
比如 animal.eats( Animal a)。
这里的Animal是个函数参数,函数参数支持参数类型不同的重载,编译器只能重载!!!,参数是个基类Animal 类型。而我们的实现中没有这个方法,无法找到成员函数实现:bool eats( Animal a);
这是没有意义的。熊吃动物吗?只有知道具体的才行,而编译器看到 Animal参数只会去找对应的函数实现。
熊的实现一般如下:
class Bear : Animal{
bool eats( Cat cat);
bool eats( Dog cat);
bool eats( Animal a);//这个没有
};
可以看到没有 eats(Animal a)这个成员函数。因此会导致编译错误。
a是个基类类型,编译器无法判定这个函数重载的是哪个:是dog,还是Cat。
多态只有在运行时才能体现出来。
也就是说函数参数不同自动去推导它的具体实现类是哪个,因为只有运行时才知道。
而 像
Animal animal= bear();
animal.move();
这个animal不是参数,是真正的实体类,调用实体类的方法,首先编译器在接口中 是可以找到这个接口定义的。另外在运行时,是可以通过vtable找到多态实现。前面的函数参数是不行的。
第一种方法解决:
既然编译器找不到 eats(Animal a) 的实现。在Animal接口中添加实现。
class Animal: Animal{
bool eats( Animal a){
}
}
这样就可以通过编译了。
这时用 a.eatenBy(*this),a是运行行多态,可以确定类型。*this也是具体实现,可以得到类型。即双分派。 animal.eats这个是第一个动态;a.eatenBy是第二次。
第二种,采用奇异模板 CRTP
将子类作为模板参数传给基类,让基类把参数强制cast。
第三种,c++23
1//// Before
2// CRTP
3template <class Derived>
4struct Base {
5 void foo() {
6 auto& self = *static_cast<Derived*>(this);
7 self.bar();
8 }
9};
10
11struct Derived : Base<Derived> {
12 void bar() const {
13 std::cout << "CRTP Derived\n";
14 }
15};
16
17////////////////////////////////////////////
18//// After
19// Deducing this
20struct Base {
21 template <class Self>
22 void foo(this Self& self) {
23 self.bar();
24 }
25};
26
27struct Derived : Base {
28 void bar() const {
29 std::cout << "Deducing this Derived\n";
30 }
31};

浙公网安备 33010602011771号