Overriding Qualified Virtual Methods
Asked Answered
T

4

6

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?

Thundersquall answered 30/3, 2011 at 2:53 Comment(1)
You could also override the behaviours in separate classes: DerivedA : public BaseA, DerivedB : public BaseB, and then Derived : public DerivedA, public DerivedB. This doesn't solve the ambiguity problem, thoughSparker
R
3

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() {}
};
Regret answered 30/3, 2011 at 3:18 Comment(3)
Thanks for the info on the MS syntax; unfortunately it only works with abstract base classes (note the "__interface" keyword in their example).Thundersquall
@John: The managed variant works for virtual methods inherited from base classes. Anyway, take a look at my edit, I think this should solve your problem.Regret
Thanks again. Your suggestion would work, but the details of my situation prevent me from using it. Basically, I'm writing a class definition for some reverse-engineered code and I literally can't change the inheritance structure or the composition of the virtual function table (or use managed C++, obviously)Thundersquall
B
2

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.

Bouldon answered 30/3, 2011 at 3:4 Comment(0)
B
1

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.

Bhili answered 30/3, 2011 at 3:3 Comment(0)
S
0

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;
Saltworks answered 16/9, 2014 at 10:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.