覆盖虚函数时的奇怪语法

j_k*_*bik 15 c++ syntax overriding language-lawyer

阅读这个答案,我很惊讶,如果它真的像所描述的那样工作,那就试试吧:

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

C
C
C
Run Code Online (Sandbox Code Playgroud)

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

编辑:

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

Tar*_*ama 16

我认为C::A::foo定义的事实A::foo是由于注入类名.

N3337 [class]/2:在看到类名后立即将类名插入到作用域中.类名也插入到类本身的范围内; 这被称为注入类名.出于访问检查的目的,inject-class-name被视为公共成员名称.[...]

既然A是基类,C并且名称A被注入到范围内A,A也可以从中看到C.

鉴于上述情况,我们可以做出可怕的反常事情:

void C::C::C::A::A::A::foo(){
    std::cout << "A" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

Live Demo


Mak*_*jov 7

有趣的是,

void C::A::foo(){
  std::cout << "A" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

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

void A::foo(){
  std::cout << "Base A" << std::endl;
}
void C::A::foo(){
  std::cout << "A" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

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

就标准所说的内容而言,我目前正在努力找到在标准海中何时适当的确切定义,但我可以在第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
Run Code Online (Sandbox Code Playgroud)

该示例与构造函数解析有关,但构造函数最终是一种方法,因此我的猜测是GCC对每个范围分辨率使用相同的机制.