Cannot instantiate abstract class: Why is the template parameter (reference) causing this?

1k views Asked by At

I am having trouble with some code.

'Bar' : cannot instantiate abstract class

I have (finally) been able to recreate the error in a small amount of code.

struct SomeStruct
{
    // ******
};

template <typename TIN, typename TOUT, typename TINDEX>
struct IFoo
{
public:
    virtual void add(const TIN item) = 0; // <-- BAD
    //virtual void add(const TOUT& item) = 0; // <-- GOOD

    // ******
};

template <typename TVALUE, typename TINDEX>
struct Bar : IFoo<TVALUE &, TVALUE, TINDEX>
{

public:

    void add(const TVALUE& item)
    {
        // ******
    }

    // ******
};

int main(int argc, char *argv[])
{
    SomeStruct someStruct;
    Bar<SomeStruct, int> bar = Bar<SomeStruct, int>();
    bar.add(someStruct);

    // ******
}

Can anyone please advise WHY using a reference with the template parameter is causing this?

3

There are 3 answers

0
TartanLlama On BEST ANSWER

The issue here is that when you write const TIN and TIN is a reference type, the const applies to the reference and not to the value type.

This is why you are seeing different behaviour for const TIN and const TOUT& even when you think they should be the same.

A simple fix for this is to add the const to the value type in your IFoo instantiation:

struct Bar : IFoo<const TVALUE &, TVALUE, TINDEX>
//          here  ^^^^
2
Pandrei On

Your problem goes back to basic concepts like function signature: a function signature/prototype is given by the function name, the number of parameters, datatype of parameters and the order of appearance of those parameters.

For any two functions if any of the above differ, than you are dealing with two different functions.

More exactly, these two represent two different signatures:

virtual void add(const TIN item) = 0; 
virtual void add(const TOUT& item) = 0;

Since you implement only the second one in the derived class, you get the error.

0
Barry On

We can simply your example further to:

template <typename T>
struct IFoo {
    virtual void add(const T item) = 0;
};

template <typename T>
struct Bar : IFoo<T&> {
    void add(const T& item) { }
};

In Bar, you are taking item as a reference to const T. In IFoo, you are declaring a pure virtual method that takes a const reference to T. But all references are inherently const, so that's redundant - it's equivalent to just taking a reference to T.

For Bar<int> - the signature of IFoo::add() is void add(int& ) whereas Bar::add() is void add(const int& ). Those signatures don't match - hence Bar is still an abstract class. Just one that hid IFoo::add().

If you have a C++11 compiler, you should prefer to add the override keyword to Bar::add() so that you would get a compiler error for:

main.cpp:15:10: error: 'void Bar<T>::add(const T&) [with T = int]' marked 'override', but does not override
     void add(const T& ) override { }
          ^