Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weird syntax when overriding virtual functions

Reading this answer, I was surprised enough to try out for myself if it really works as described:

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

I really did not think that it was possible to override a virtual method from two different base classes, that have the same name and signature. And, as I expected, above program prints:

C
C
C

So the answer is wrong - as I felt from the start. Surprising part is: why does my gcc accept this syntax: void C::A::foo(){? I could not find anything on what that could mean. Is this a gcc bug/feature? Is it some obscure standard C++ syntax? Or am I misinterpreting the situation completely?

EDIT:

It seems that void C::A::foo(){} is just a definition for A::foo in this context. But why? Is this GCC bug? Or is this somehow permitted by the standard? If so, is it specific rule for this kind of thing, or some kind of generic clause (say: if an identifier doesn't make sense - like 'C::A::foo', then compiler is free to do what it wants).

like image 269
j_kubik Avatar asked Sep 16 '15 07:09

j_kubik


People also ask

What happens if we don override virtual function in inheritance?

If you don't override a pure virtual function in a derived class, that derived class becomes abstract: class D2 : public Base { // no f1: fine.

How is virtual function different from overriding?

Because virtual functions are called only for objects of class types, you cannot declare global or static functions as virtual . The virtual keyword can be used when declaring overriding functions in a derived class, but it is unnecessary; overrides of virtual functions are always virtual.

Do I need to override virtual functions?

It is not mandatory for the derived class to override (or re-define the virtual function), in that case, the base class version of the function is used. A class may have virtual destructor but it cannot have a virtual constructor.

Can we override pure virtual function?

Yes !! It improves code clarity: override keyword prevents ambiguity and convey it's meaning of overriding its base class method.


3 Answers

I think the fact that C::A::foo defines A::foo is due to the 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. [...]

Since A is a base class of C and the name A is injected into the scope of A, A is also visible from C.

Given the above, we can do horribly perverse things like:

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

Live Demo

like image 157
TartanLlama Avatar answered Oct 11 '22 12:10

TartanLlama


Interestingly enough,

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

defines A::foo() (you can provide an implementation for a pure virtual function if you want to). You can check that this is really the case by adding an additional definition:

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

GCC will report a multiple definition error. This probably makes sense, because you can look at C::A as a name alias for A.

In terms of what the standard says, I'm currently struggling to find the exact definition of when this is appropriate in the sea of standardese, but I can see a related example in 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

The example is to do with constructor resolution, but constructor is ultimately a method, so my guess is that GCC used the same mechanism for every scope resolution.

like image 22
Maksim Solovjov Avatar answered Oct 11 '22 13:10

Maksim Solovjov


The Visual Studio compiler (2008) does not allow

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

but only

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

as it is completely valid to provide a definition for pure virtual methods.

"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.

See here (forum.codeguru.com).

A pure virtual function cannot be called dynamically, but statically:

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

Explanation:

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(); }

See here (isocpp.org).

like image 21
Gombat Avatar answered Oct 11 '22 13:10

Gombat