双重派发 双重委派 双派发 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

借助Deducing this,可以简化CRTP
 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};
这种新的方式实现CRTP,可以省去CR,甚至是T,要更加自然,更加清晰。
posted @ 2022-12-17 10:39  Bigben  阅读(200)  评论(0)    收藏  举报