编译器如何在内部解决c++中的菱形问题

How does the compiler internally solve the diamond problem in C++?

本文关键字:问题 c++ 在内部 解决 编译器      更新时间:2023-10-16

我们知道可以使用虚拟继承来解决菱形问题。

例如:

   class Animal // base class
   {
     int weight;
     public:
     int getWeight() { return weight;};
   };
   class Tiger : public Animal { /* ... */ }; 
   class Lion : public Animal { /* ... */ };
   class Liger : public Tiger, public Lion { /* ... */ }; 
   int main()
   {
     Liger lg ;
     /*COMPILE ERROR, the code below will not get past
     any C++ compiler */
     int weight = lg.getWeight();
   }
当我们编译这段代码时,会得到一个歧义错误。现在我的问题是编译器如何在内部检测这种歧义问题(菱形问题)。

编译器构建了列出每个类的所有成员的表,并且还提供了允许它在任何类的继承链上上下移动的链接。

当需要定位成员变量(在您的示例中是weight)时,编译器从实际的类(在您的示例中是Liger)开始。它不会在那里找到权重成员,所以它会向上移动一层到父类。在本例中有两个,因此它同时扫描Tiger和Lion以查找name weight的成员。仍然没有任何命中,所以现在它需要再上升一级,但它需要做两次,这一层的每个类一次。直到在继承树的某个级别找到所需的成员为止。如果在任何给定的级别上,它只找到一个成员,考虑所有的多个继承分支,一切都很好,如果它发现两个或更多的成员具有所需的名称,那么它无法决定选择哪一个,因此它会出错。

当编译器为一个类创建函数指针表时,每个符号必须在该表中只出现一次。在这个例子中,getWeight出现了两次:在TigerLion中(因为Liger没有实现它,所以它要在树中查找它),因此编译器卡住了。

其实很简单

在您的代码中,liger的结构是

Liger[Tiger[Animal]Lion[Animal]]

如果从Liger指针调用Animal函数,实际上有两种动物可以转换(因此有歧义)

虚拟继承将生成一个类似

的结构
Liger[Tiger[*]Lion[Animal]]
            -----/

现在只有一个Animal,从两个碱基都可以间接到达,所以从Liger到Animal的转换不再是模糊的。

编译器不检测歧义问题。编译器只对代码中使用的内容的声明感兴趣。如果一个元素声明了多次,编译器不会报错。

歧义问题来自链接器。链接器在对象lg的继承树中看到getWeight()函数的两个定义,并且不知道该选择哪个定义与调用lg.getWeight()进行链接。这就是歧义