覆盖虚函数时的奇怪语法

Weird syntax when overriding virtual functions

正在阅读 this answer,如果它真的像描述的那样有效,我很惊讶自己尝试一下:

#include <iostream>

class A {
public:
    virtual void foo() = 0;
};

class B {
public:
    virtual void foo() = 0;
};

class C : public A, public B {
public:
    virtual void foo();
};

void C::foo(){
  std::cout << "C" << std::endl;
}
void C::A::foo(){
  std::cout << "A" << std::endl;
}
void C::B::foo(){
  std::cout << "B" << std::endl;
}

int main() {
    C c;
    static_cast<A*>(&c)->foo();
    static_cast<B*>(&c)->foo();
    c.foo();
    return 0;
}

我真的不认为可以从两个具有相同名称和签名的不同基 类 覆盖虚拟方法。而且,正如我所料,上面的程序打印:

C
C
C

所以答案是错误的 - 正如我从一开始就感觉到的那样。令人惊讶的部分是:为什么我的 gcc 接受这种语法:void C::A::foo(){?我找不到任何关于这可能意味着什么的信息。这是 gcc bug/feature 吗?它是一些晦涩难懂的标准 C++ 语法吗?还是我完全误解了情况?

编辑:

似乎 void C::A::foo(){} 只是 A::foo 在这种情况下的定义。但为什么?这是 GCC 错误吗?还是标准以某种方式允许这样做?如果是这样,它是针对此类事物的特定规则,还是某种通用子句(比如:如果标识符没有意义 - 如 'C::A::foo',那么编译器可以自由地做它想做的事)。

有趣的是,

void C::A::foo(){
  std::cout << "A" << std::endl;
}

定义A::foo()(如果需要,您可以提供纯虚函数的实现)。您可以通过添加附加定义来检查是否确实如此:

void A::foo(){
  std::cout << "Base A" << std::endl;
}
void C::A::foo(){
  std::cout << "A" << std::endl;
}

GCC 将报告多重定义错误。这可能是有道理的,因为您可以将 C::A 视为 A 的别名。

就标准所说的而言,我目前正在努力寻找在标准语海洋中何时适用的确切定义,但我可以在 Paragraph 3.4.3.1.2 中看到相关示例:

struct A { A(); };
struct B: public A { B(); };
A::A() { }
B::B() { }
B::A ba; // object of type A // <-- here
A::A a; // error, A::A is not a type name
struct A::A a2; // object of type A

该示例与构造函数解析有关,但构造函数最终是一种方法,因此我猜测 GCC 对每个作用域解析都使用相同的机制。

我认为 C::A::foo 定义 A::foo 的事实是由于 injected-class-name.

N3337 [class]/2: A class-name is inserted into the scope in which it is declared immediately after the class-name is seen. The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name is treated as if it were a public member name. [...]

由于 AC 的基础 class 并且名称 A 被注入到 A 的范围内,AC.

也可见

鉴于上述情况,我们可以做一些非常反常的事情,例如:

void C::C::C::A::A::A::foo(){
    std::cout << "A" << std::endl;
}

Live Demo

Visual Studio 编译器 (2008) 不允许

void C::A::foo(){
  std::cout << "A" << std::endl;
}
void C::B::foo(){
  std::cout << "B" << std::endl;

但只有

void A::foo(){
  std::cout << "A" << std::endl;
}
void B::foo(){
  std::cout << "B" << std::endl;

因为为纯虚拟方法提供定义是完全有效的。

"Effective C++" Meyers mentions a reason for a pure virtual function to have a body: Derived classes that implement this pure virtual function may call this implementation smwhere in their code. If part of the code of two different derived classes is similar then it makes sense to move it up in the hierarchy, even if the function should be pure virtual.

here (forum.codeguru.com)

纯虚函数不能动态调用,只能静态调用:

C c;
static_cast<A*>(&c)->foo(); // prints C
static_cast<B*>(&c)->foo(); // prints C, dynamic dispatch
c.foo();                    // prints C, dynamic dispatch
c.A::foo();                 // prints A, static dispatch
c.B::foo();                 // prints B, static dispatch

解释:

When you call a virtual function using its fully-qualified name (the class-name followed by “::”), the compiler does not use the virtual call mechanism, but instead uses the same mechanism as if you called a non-virtual function. Said another way, it calls the function by name rather than by slot-number. So if you want code within derived class Der to call Base::f(), that is, the version of f() defined in its base class Base, you should write:

void Derived::f()
{ Base::f(); }

参见 here (isocpp.org)