26

I tried this code:

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

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

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

    virtual void A::foo();
    virtual void B::foo();
};

void C::A::foo(){}
void C::B::foo(){}

int main()
{
    C c;
    return 0;
}

It is OK when using the commented part, but when I try to write the definitions outside the class declaration, the compiler reports errors. I am using the MSVC11 compiler, does anyone know how to write this? I need to move the code into the cpp file.

Thank you~~

gx_
  • 4,690
  • 24
  • 31
watson
  • 395
  • 1
  • 5
  • 14
  • 1
    Commented part is also not working in gcc. – Nemanja Boric Aug 23 '13 at 08:39
  • 4
    That makes no sense at all. It should just be `virtual void foo();`, and just *once*. – Kerrek SB Aug 23 '13 at 08:40
  • How do you want to _use_ `A`, `B` and `C`? There are many possibilities: [example 1](http://ideone.com/KlVTgv), [example 2](http://ideone.com/R2SyTz), ... (those probably define more functions than necessary) – gx_ Aug 23 '13 at 09:21
  • 1
    [dcl.meaning]/1 Forbids the use of a *qualified-id* in the declaration of a (member) function: "When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers [...]"; therefore any `virtual void X::foo();` is illegal (as a declaration inside `C`). – dyp Aug 23 '13 at 10:35
  • Actually after [DyP's remark](http://stackoverflow.com/questions/18398409/c-inherit-from-multiple-base-classes-with-the-same-virtual-function-name#comment27025715_18399065) the code linked in my previous comment is misleading, because `C::A::foo` really means `A::foo` (thanks @DyP ). Here are simpler examples: http://ideone.com/HCYM1X and http://ideone.com/vdRp3X . – gx_ Aug 23 '13 at 10:54

5 Answers5

33

A function overrides a virtual function of a base class based on the name and parameter types (see below). Therefore, your class C has two virtual functions foo, one inherited from each A and B. But a function void C::foo() overrides both:

[class.virtual]/2

If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list, cv-qualification, and ref-qualifier (or absence of same) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides Base::vf.

As I already stated in the comments, [dcl.meaning]/1 forbids the use of a qualified-id in the declaration of a (member) function:

When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers [...]"

Therefore any virtual void X::foo(); is illegal as a declaration inside C.

The code

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

is the only way AFAIK to override foo, and it will override both A::foo and B::foo. There is no way to have two different overrides for A::foo and B::foo with different behaviour other than by introducing another layer of inheritance:

#include <iostream>

struct A
{
    virtual void foo() = 0;
};

struct B
{
    virtual void foo() = 0;
};

struct CA : A
{
    virtual void foo() { std::cout << "A" << std::endl; }
};

struct CB : B
{
    virtual void foo() { std::cout << "B" << std::endl; }
};

struct C : CA, CB {};

int main() {
    C c;
    //c.foo();  // ambiguous

    A& a = c;
    a.foo();

    B& b = c;
    b.foo();
}
Melebius
  • 6,183
  • 4
  • 39
  • 52
dyp
  • 38,334
  • 13
  • 112
  • 177
  • If `CA::foo` and `CB::foo` both need to access shared data within the object, you may need a *third* base class to contain the shared part - and then you get the dreaded diamond inheritance. What a mess! – Mark Ransom Jun 15 '18 at 14:39
  • 5
    Note that abiguity can be explicitly resolved: `c.A::foo();` – ManuelSchneid3r Jan 25 '19 at 15:03
  • What is the difference if I put `virtual` before all `foo` methods or just once? Does it matters if i put virtual to the method A::foo or B::foo or CA::foo , etc ? or it is irrelevant ? – Cătălina Sîrbu Mar 21 '20 at 19:15
  • 2
    @CătălinaSîrbu A function is virtual if either you explicitly write `virtual` or if it has the same signature (name, parameters) as a _virtual_ function in any base class. So most `virtual` keywords here are redundant. I prepend `virtual` to all virtual functions - even if it is not necessary - to show directly that the function is virtual. – dyp Apr 04 '20 at 18:51
4

You've got just one virtual function foo:

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

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

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

};

void C::foo(){}
void C::A::foo(){}
void C::B::foo(){};

int main() {
    C c;
    return 0;
}
Paul Evans
  • 27,315
  • 3
  • 37
  • 54
  • 4
    *Just a remark:* The definitions `void C::A::foo(){}` and `void C::B::foo(){}` provide definitions for the pure virtual functions `A::foo` and `B::foo`, respectively (and are not required). – dyp Aug 23 '13 at 10:37
  • as say **dyp** : `void C::foo() {cout<<"C"< – biv Oct 15 '16 at 08:08
  • @dyp Just curious why it's legal to use syntax `void C::A::foo(){}` to provide definition for `A::foo()`, any benefits to use this kind of syntax rather than overriding `A::for(){ }` directly? It looks very confusing to me, what if `A` is an inner class? I compiled this code and was surprised it's legal. – Dreamer Feb 06 '17 at 02:14
  • @Dreamer It's a consequence of general concepts. When you define a member function, you simply use one of the ways to refer to the name of the member; the same ways of referring can be used when you want to call the function, e.g. `this->C::A::foo();` The name `A` is a member of `C` because class `A` implicitly defines a member named `A` referring to itself (injected-class-name). This member `A::A` is visible inside of `C` because of the inheritance. – dyp Feb 06 '17 at 11:46
3

I stepped into the same problem and accidentially opened a second thread. Sorry for that. One way that worked for me was to solve it without multiple inheritance.

#include <stdio.h>

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

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


class C
{
    class IA: public A
    {
        virtual void foo(void)
        {
            printf("IA::foo()\r\n");
        }
    };
    class IB: public B
    {
        virtual void foo(void)
        {
            printf("IB::foo()\r\n");
        }
    };

    IA m_A;
    IB m_B;
public:
    A* GetA(void)
    {
        return(&m_A);
    }

    B* GetB(void)
    {
        return(&m_B);
    }
};

The trick is to define classes derived from the interfaces (A and B) as local classes (IA and IB) instead of using multiple inheritance. Furthermore this approach also opens the option to have multiple realizations of each interface if desired which would not be possible using multiple inheritance. The local classes IA and IB can be easily given access to class C, so the implementations of both interfaces IA and IB can share data.

Access of each interface can be done as follows:

main()
{
    C test;
    test.GetA()->foo();
    test.GetB()->foo();
}

... and there is no ambiguity regarding the foo method any more.

TLI123
  • 31
  • 2
1

You can resolve this ambiguity with different function parameters.

In real-world code, such virtual functions do something, so they usually already have either:

  1. different parameters in A and B, or
  2. different return values in A and B that you can turn into [out] parameters for the sake of solving this inheritance problem; otherwise
  3. you need to add some tag parameters, which the optimizer will throw away.

(In my own code I usually find myself in case (1), sometimes in (2), never so far in (3).)

Your example is case (3) and would look like this:

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

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

class C : public A, public B
{
    void foo(tag_a) override;
    void foo(tag_b) override;
};
adigostin
  • 633
  • 7
  • 20
0

A slight improvement over adigostin's solution:


#include <iostream>

struct A {
    virtual void foo() = 0;
};

struct B {
    virtual void foo() = 0;
};

template <class T> struct Tagger : T {
    struct tag {};
    void foo() final { foo({}); }
    virtual void foo(tag) = 0;
};

using A2 = Tagger<A>;
using B2 = Tagger<B>;

struct C : public A2, public B2 {
    void foo(A2::tag) override { std::cout << "A" << std::endl; }
    void foo(B2::tag) override { std::cout << "B" << std::endl; }
};

int main() {
    C c;
    A* pa = &c;
    B* pb = &c;
    pa->foo(); // A
    pb->foo(); // B
    return 0;
}

Assuming that the base classes A and B are given and cannot be modified.

Hank
  • 310
  • 1
  • 5