构建虚拟基础时的编译器行为 class

Compiler behaviour when constructing virtual base class

考虑这段代码:

#include <iostream>

class A {
  public:
    A(int s) { std::cout << "A(" << s << ")\n"; }
};

class B1 : virtual public A {
  public:
    B1(int s1, int s2)
        : A{s1} { std::cout << "B1(" << s1 << "," << s2 << ")\n"; }
};

class B2 : virtual public A {
  public:
    B2(int s1, int s2)
        : A{s1} { std::cout << "B2(" << s1 << "," << s2 << ")\n"; }
};

class C1 : public B1, public B2 {
  public:
    C1() : B1{1,2}, B2{3,4}, A{5} {}
};

class C2 : public B1, public B2 {
  public:
    C2() : B1(1,2), B2(3,4), A{5} {}
};

int main()
{
    std::cout << "Create c1:\n";
    C1 c1;

    std::cout << "\n";
    std::cout << "Create c2:\n";
    C2 c2;

}

Class A 是 B1 和 B2 的虚拟基 class。 Classes C1和C2完全相同,只是C1使用{...},C2使用(...)构造B1和B2。

由于这里使用了虚拟继承,classA应该作为C1或C2构造的一部分来构造。

如果我使用 Microsoft VS2015 编译此代码,它会在 运行:

时产生此输出
Create c1:
A(5)
B1(1,2)
B2(3,4)

Create c2:
A(5)
B1(1,2)
B2(3,4)

这正是我所期望的。

但是如果我用 GCC (6.1.0) 编译它会产生这个输出:

Create c1:
A(5)
A(1)
B1(1,2)
A(3)
B2(3,4)

Create c2:
A(5)
B1(1,2)
B2(3,4)

这里A的构造函数在构造c1的时候调用了3次,在构造c2的时候只调用了一次。

这是 GCC 中的错误还是我误解了什么?

不太清楚为什么会这样,但 A class 不应该是多态的(至少有一个虚函数)以正确地虚拟继承它吗?!

这个bug更严重:

    #include <iostream>

    class A {
       public:
          A(int s) { std::cout << "A(" << s << ")\n"; ms = s; };
          int ms;
          virtual void dummy() {std::cout << "Aaaaaa!" << std::endl;};
          virtual ~A() {std::cout << "~A(" << ms << ")\n";};
    };

    class B1 : virtual public A {
        public:
           B1(int s1, int s2)
              : A{s1} { std::cout << "B1(" << s1 << "," << s2 << ")\n"; };
    };

    class B2 : virtual public A {
        public:
           B2(int s1, int s2)
              : A{s1} { std::cout << "B2(" << s1 << "," << s2 << ")\n"; };
    };

       class C1 : public B1, public B2 {
           public:
              C1() : B1{1,2}, B2{3,4}, A{5}  {};
       };

       class C2 : public B1, public B2 {
           public:
              C2() : B1(1,2), B2(3,4), A{5} {};
       };

      int main()
      {
         {
             std::cout << "Create c1:\n";
             C1 c1;
             std::cout << "Calling A's dummy on c1: " << std::endl;
             c1.dummy();
             std::cout << "Size of c1: " << sizeof(c1) << std::endl;
         }

         {
             std::cout << "\n";
             std::cout << "Create c2:\n";
             C2 c2;
             std::cout << "Calling A's dummy on c2: " << std::endl;
             c2.dummy();
             std::cout << "Size of c2: " <<sizeof(c2) << std::endl;
         }

     }

在 GCC 5.4.0 上编译 运行 此代码产生以下输出:

  • 创建 c1:
  • A​​(5)
  • A​​(1)
  • B1(1,2)
  • A​​(3)
  • B2(3,4)
  • 在 c1 上调用 A 的虚拟对象:
  • 啊啊啊!
  • c1 的大小:32
  • ~A(3)

  • 创建c2:
  • A​​(5)
  • B1(1,2)
  • B2(3,4)
  • 在 c2 上调用 A 的虚拟对象:
  • 啊啊啊!
  • c2 的大小:32
  • ~A(5)

回答我自己的问题:

显然,GCC 在这种情况下确实存在错误。使用 GCC 版本 7.0.0 编译代码会产生正确的行为,输出如下:

Create c1:
A(5)
B1(1,2)
B2(3,4)

Create c2:
A(5)
B1(1,2)
B2(3,4)