覆盖虚函数时的奇怪语法

时间:2015-09-16 07:56:52

标签: c++ syntax override language-lawyer

阅读this answer,我很惊讶,如果它真的按照描述运作的话,可以自己试试:

item.IsSelected = false;

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

#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;
}

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

编辑:

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

3 个答案:

答案 0 :(得分:16)

我认为C::A::foo定义A::foo的事实是由注入类名引起的。

  

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

由于AC的基类,并且名称A被注入A的范围,A也可以从{{C看到1}}。

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

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

Live Demo

答案 1 :(得分:7)

有趣的是,

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对每个范围分辨率使用相同的机制。

答案 2 :(得分:2)

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;

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

  

“有效的C ++”迈耶斯提到了纯虚函数的原因   拥有一个正文:实现这个纯虚拟的派生类   函数可以在其代码中调用此实现。如果是部分   两个不同的派生类的代码是相似的,然后它   在层次结构中将其向上移动的意义,即使该功能应该是   纯虚拟。

请参阅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

说明:

  

使用完全限定名称调用虚拟函数时(   class-name后跟“::”),编译器不使用virtual   调用机制,但使用与调用a相同的机制   非虚函数。换句话说,它通过名称调用函数   而不是插槽号。所以,如果你想在派生类中的代码   Der调用Base :: f(),即在其基数中定义的f()版本   class Base,你应该写:

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

请参阅here (isocpp.org)

相关问题