John
John

Reputation: 127

Overriding Qualified Virtual Methods

I have C++ class with multiple parents; each parent defines a function with a common name but a different purpose:

class BaseA
{
    virtual void myFunc();  // does some task
};
class BaseB
{
    virtual void myFunc();  // does some other task
};
class Derived : public BaseA, public BaseB;

If that was it, I would have no problem - I could resolve the ambiguity it with a using statement, and I could choose which one to call using the base class names and the scope resolution operator.

Unfortunately, the derived class needs to override them both:

class Derived : public BaseA, public BaseB
{
    virtual void BaseA::myFunc(); // Derived needs to change the way both tasks are done
    virtual void BaseB::myFunc();
}

This doesn't work, not because it introduces a new ambiguity (although it may), but because

"error C3240: 'myFunc' : must be a non-overloaded abstract member function of 'BaseA'"

"error C2838: illegal qualified name in member declaration"

Under different circumstances I might just rename the methods, or make them pure virtual as the compiler suggests. However, the class hierarchy structure and a number of external issues make the first option extremely difficult, and the second impossible.

Does anyone have a suggestion? Why are qualifiers only allowed for pure virtual methods? Is there any way to simultaneously override virtual methods and resolve ambiguities?

Upvotes: 6

Views: 3466

Answers (4)

Richard Hodges
Richard Hodges

Reputation: 69902

I realise that this question is old, but it's had a lot of views and there is a clean way to solve this if you are the author of the interfaces.

Many people believe that virtual interfaces should have public non-virtual functions that defer to private virtual functions internally (I agree with them). This has a few advantages, one of them being that non-virtual names can have distinct meanings since they are more strongly bound to the interface:

struct BaseA
{
  virtual ~BaseA() = default;

  void func() 
  {
    handle_a_func();
  }

private:
  virtual void handle_a_func() = 0;
};

struct BaseB 
{
  virtual ~BaseB() = default;

  int func() const  // note the different signature and return type
  {
    handle_b_func();
  }

private:
  virtual int handle_b_func() const = 0;
};

// now provide an implementation

struct ABImpl : public BaseA, public BaseB
{
  ABImpl() {}

private:
  void handle_a_func() override final 
  {
    // alter some state
  }

  int handle_b_func() const override final
  {
    return _x;
  }

  int _x = 0;
};        

// now use the class
auto ab = make_shared<ABImpl>();

auto a = static_pointer_cast<BaseA>(ab);
auto b = static_pointer_cast<const BaseB>(ab);

a->func();  // uses A's mutable interface
auto x = b->func();  // uses B's const interface

Another advantage of this approach is that base class implementations can manage things like mutexes and sentinels on behalf of the derived functions, for example:

struct base {

  void do() {
    std::unique_lock<std::mutex> l(_m);
    handle_do();
  }

private:
  virtual void handle_do() = 0;

  std::mutex _m;
};

Yet another advantage is that some useful free-function operators need only be defined once for the entire class hierarchy:

struct base
{
  void write_to(std::ostream& os) const {
    // lock a mutex here?
    handle_write(os);
  private:
    virtual void handle_write(std::ostream& os) const {
      os << "write base class info here\n";
    }
};

inline std::ostream& operator<<(std::ostream& os, const base& b) {
  b.write_to(os);
  return os;
}

struct derived : base {
private:
  virtual void handle_write(std::ostream& os) const override {
    base::handle_write(os);  // handle base class implementation
    std::cout << "write relevant data here. We could still be overridden safely\n";
  }
};

// write any derived class like this:
auto b = unique_ptr<base> { new derived() };
cout << "here is a kind-of b:\n" << *b << endl;

Upvotes: 0

Ben Voigt
Ben Voigt

Reputation: 283773

Microsoft allows that syntax (it's available beginning in Visual C++ 2005). They also introduced a new, more powerful syntax for managed code only.

Neither one was included in C++0x.

See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2108.html


I think this is a workaround:

class BaseA
{
protected:
    virtual void myFunc();  // does some task
};
class BaseB
{
protected:
    virtual void myFunc();  // does some other task
};
class ShimA : virtual BaseA
{
    virtual void myFunc() { myFuncA(); }
protected:
    virtual void myFuncA() { BaseA::myFunc(); }
};
class ShimB : virtual BaseB
{
    virtual void myFunc() { myFuncB(); }
protected:
    virtual void myFuncB() { BaseB::myFunc(); }
};
class Derived : public virtual BaseA, public virtual BaseB, protected ShimA, protected ShimB
{
     virtual void myFuncA() {}
     virtual void myFuncB() {}
};

Upvotes: 3

Puppy
Puppy

Reputation: 146988

You could use a composition object.

class Derived : public BaseB {        
    struct temp : public BaseA {
        virtual void myFunc() {
             d->BaseAMyFunc();
        }
        Derived* d;
    };
    temp t;
public:
    Derived() {
        t.d = this;
    }
    operator BaseA&() { return temp; }
    operator const BaseA&() const { return temp; }
    void myFunc(); // refers to BaseB::myFunc()
    void BaseAMyFunc(); // called when BaseA::myFunc() is called.
}

This isn't especially neat and it's somewhat restricted, but it does work some circumstances.

Upvotes: 2

Suroot
Suroot

Reputation: 4423

This is one of the big issues with multiple inheritance. There are always issues when you have multiple functions of the same name being inherited to determine which one is supposed to be overridden see The Diamond Problem. You cannot override both as the function syntax (function name and operators) must be unique.

Upvotes: 1

Related Questions