tags:

views:

91

answers:

2

I've got a interface that's implemented as an abstract base class with a number of pure virtual public methods. These pure virtual functions can be implemented using a template since the differences between subclasses aren't large - so my idea was to use multiple inheritance to mix-in the appropriately templated helper-class that provides the implementation. However, the compiler complains that the base class is abstract; it isn't considering the helper mix-in's implementation so thinks there's no implementation of a required method.

For example:

class TrivialList {
    int count;
public:
    TrivialList(int count) : count(count){}
    virtual double Average() const=0;
    int Count() const {return count;}
    virtual ~TrivialList(){}
};
template<typename TIndexable> class AverageHelper {
public:
    double Average() const {
        TIndexable const & self = static_cast<TIndexable const &>(*this);
        double sum=0.0;
        for(int i=0;i<self.Count();++) sum += self.Get(i);
        return sum / self.Count();
    }
};
class IndexableList : public TrivialList, public AverageHelper<IndexableList> {
    std::vector<double> backend;
public:
    IndexableList(int count) : TrivialList(count), backend(count) { }
    double & Get(int i) { return backend[i];}
    double const & Get(int i) const { return backend[i];}
};
IndexableList * MakeList() {return new IndexableList(5);} //error!
//    cannot instantiate abstract class

I'm using MSC 10.0 (Visual Studio 2010); the code fails with a similar error using g++ 4.5.

Get or the real-world equivalents in my project cannot be virtual because they're extremely minor operations that need to be inlined for adequate performance (think put-pixel/get-pixel) - so I need the generic algorithms to be templated rather than generic via virtual function calls.

+1  A: 

It doesn't work because AverageHelper<>::Average() doesn't override TrivialList::Average(). In order to override a virtual function, the overriding class must inherit from the class containing the function to be overridden.

You could change your template thus:

template<typename TIndexable, typename Base > 
class AverageHelper : public Base {
public:
  template< typename T >
  AverageHelper(T arg) : Base(arg) {}
  // ... 
};

class IndexableList : public AverageHelper<IndexableList,TrivialList> {
public:
  IndexableList(int count) : AverageHelper<IndexableList,TrivialList>(count) {}
  // ...
};

You might want to virtually derive from TrivialList:

template<typename TIndexable, typename Base > 
class AverageHelper : virtual public Base {
  // ... 
};
sbi
Does virtual inheritance have a downside?
Eamon Nerbonne
@Eamon: Access to base class sub-objects is less efficient. I guess for common implementations it's one additional indirection. In a MI scenario it's often unavoidable, though. However, sometimes you __want__ individual sub-objects for a multiply inherited base class. I guess failing to provide these could be seen as a downside of virtual inheritance. `:)`
sbi
Inheriting `AverageHelper` from `Base` fails to call `Base`'s constructor appropriately. In my example, the constructor might be removed, but in general, it'd be nice to retain the option of a non-default constructor.
Eamon Nerbonne
Thanks for the help btw! I'm leaning to getting rid of constructors and just using explicit `Init` methods instead to work around this issue.
Eamon Nerbonne
@Eamon: Abstract base classes are best without data. Without data, they usually don't need constructors. Often abstract classes are also virtual base classes and virtual base classes should have default constructors anyway.
sbi
@Eamon: Abandoning constructors in general is a bad move, because `init()` methods require two-phase construction. If programmers would always remember to adhere to initialization sequences, we wouldn't have needed someone to invent C++ in the first place. If you need two-phase construction (often when you want to call a virtual functions as part of initialization), hide it in some inner class. In this case, what's the problem with passing the `count` through the `IndexableList` class? I'll add that to my answer.
sbi
@Eamon: Oh, and if `TrivialList` is a virtual base class, it's constructor must be invoked from `IndexableList` anyway.
sbi
The "problem" is that AverageHelper - which only really needs to know about a limited subset of the interface - suddenly is tightly coupled to the constructor parameters. If I want to use it with a different base class, then I may need a different constructor pass-through. It's not a huge issue; but it feels a little dirty.
Eamon Nerbonne
If abstract base classes are best without data - what would you do in the example with the shared notion of "count" (assuming for the moment that you couldn't just extract it from the std::vector)? Copy that bit into each subclass? Wouldn't that be unnecessary code duplication?
Eamon Nerbonne
@Eamon: Maybe you need to split your inteface into several interfaces (aka abstract classes)? Then you could implement different interfaces in different classes and mix those in. Also, you could make the type of that constructor parameter a template argument. So long as you don't need perfect forwarding (which will only be available in C++1x), that would work. I have edited my answer. (Note that member functions of templates that are never instantiated don't have to compile. So you could add constructor overloads with more arguments to your template, if you don't use them with the wrong base.)
sbi
+3  A: 

For implementing mix-ins via templates, you need the template implementing the abstract function to derive from the abstract base class.

So you may fix your code by changing it the following way:

// ...
template<typename TIndexable> class AverageHelper : public TriviaList{

// ...
class IndexableList : public AverageHelper<IndexableList> {

In general, if you want to provide more than one mix-in, you may either use a virtual inheritance in order not multiplying the instances of the base classes, or to use chain inheritance as in the following sample:

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

template<class Base>
class FooImpl : Base {
public:
    void foo() { /* default foo implementation */ }
};

template<class Base>
class BarImpl : Base {
public:
    void bar() { /* default bar implementation */ }
};

class Derived : public BarImpl<FooImpl<Abstract> > {
    // You have both foo() and bar() implementations available
};
Jack Shainsky
I will need multiple mixins - chaining them sounds tricky; how would this work with virtual inheritance?
Eamon Nerbonne
If I use chaining, how can I deal with constructor parameters?
Eamon Nerbonne
Virtual inheritance works pretty straightforward (you just virtually derive each mixin from the base class and derive the real implementation from the mixins), but it causes warnings about dominance, but I usually use the chain inheritance, leaving multiple inheritance for implementing several different abstract interfaces.
Jack Shainsky
+1: thanks for the idea's; sbi's answer is a little more specifically appropriate to my needs, so I'll mark his as an answer.
Eamon Nerbonne