0

I have a hierarchy of templated classes that are constructed in one place and are passed around to other places to do some operations on them.

For example, a class could be templated to some complex object which knows how to convert itself to a double, and the templated class has an operation to output this object as a double. An example of a utility function would be a function that outputs collections of this class as a table.

However, I do not want to pass in this type as a templated class, because the utility function should work on any concrete class variant because they can all represent themselves as double. Therefore I want to have some non-templated interface that has the 'represent as double' function. Why does the following not work?

#include "stdafx.h"

class Interface1
{
public:
    virtual int ReturnSomeInt();
};

template<typename myType>
class TClass1 : public Interface1
{
public:
    int ReturnSomeInt() {return 5;}
    void CalculateSomething(myType myValue) {}

    TClass1() {}
};

//---------------------------------------------

class Interface2 : public Interface1
{
public:
    virtual double ReturnSomeDouble();
};

template<typename myType>
class TClass2 : public TClass1<myType>, public Interface2
{
public:
    double ReturnSomeDouble() {return 9.2;}
    void CalculateSomethingElse(myType myValue) {}

    TClass2() {}
};

//---------------------------------------------

int _tmain(int argc, _TCHAR* argv[])
{
    Interface2 myInterface = TClass2<float>();
    int myInt = myInterface.ReturnSomeInt();
    double myDouble = myInterface.ReturnSomeDouble();
    return 0;
}

I get a link error 2019 about the fact that it could not find the symbol Interface2::ReturnSomeDouble(void). What could be the problem?

iammilind
  • 68,093
  • 33
  • 169
  • 336
Steztric
  • 2,832
  • 2
  • 24
  • 43
  • Thanks @iammilind (and others with same solution) that did the trick. I defined `ReturnSomeInt()` in the derived class and forwarded the call on to its base - `int ReturnSomeInt() { return TClass1::ReturnSomeInt(); }` and it works – Steztric Jun 25 '12 at 08:55

4 Answers4

1

virtual function (here Interface2::ReturnSomeDouble()) cannot remain unimplemented, if the object of that type or its child types are instantiated.
Because, when the object is instantiated, it needs to populate the hidden class member vptr with the address/definition of virtual function, which is not found, so that linker error.

Either you have to define their body or make it a pure virtual function (so that definition becomes optional).

iammilind
  • 68,093
  • 33
  • 169
  • 336
0

What you have here is slicing. Your instance of TClass2 has been literally sliced when copied into variable 'myInterface' (the derived class part has been discarded). You need to instantiate TClass2 on the heap and then change 'myInterface' to be a pointer or reference for this to work.

(And as a result of the slicing, your code is calling the unimplemented base class function. If you were to have made the base class function pure virtual, then you perhaps have gotten a more helpful error message).

Community
  • 1
  • 1
Alex Wilson
  • 6,690
  • 27
  • 44
  • Thanks, this was also one of my errors. When I fixed the linker errors I got the error "cannot insantiate abstract class", which makes sense. I fixed this by instantiating a `TClass2<>` and passing it as a reference to it's interface `Interface2&` – Steztric Jun 25 '12 at 09:08
0

1 make the interface functions pure virtual by declaring them

virtual double ReturnSomeDouble() const = 0;

so that the linker doesn't look for the corresponding functions for the base class.

2 declare interface1 to be a virtual base to avoid inheriting it twice in Tclass2, though this is not critical for your code to compile

Walter
  • 44,150
  • 20
  • 113
  • 196
0

Since there were so many good answers to my question, I decided to re-post my code with all your helpful comments implemented.

#include "stdafx.h"

class Interface1
{
public:
    virtual int ReturnSomeInt()=0;
};

template<typename myType>
class TClass1 : public Interface1
{
public:
    int ReturnSomeInt() {return 5;}
    void CalculateSomething(myType myValue) {}

    TClass1() {}
};

//---------------------------------------------

class Interface2 : public Interface1
{
public:
    virtual double ReturnSomeDouble()=0;
};

template<typename myType>
class TClass2 : public TClass1<myType>, public Interface2
{
public:
    int ReturnSomeInt() { return TClass1<myType>::ReturnSomeInt(); }
    double ReturnSomeDouble() {return 9.2;}
    void CalculateSomethingElse(myType myValue) {}

    TClass2() {}
};

//---------------------------------------------

void WriteClassToConsole(Interface2& myInterface)
{
    std::cout << "My int:\t" << myInterface.ReturnSomeInt() << std::endl;
    std::cout << "My dbl:\t" << myInterface.ReturnSomeDouble() << std::endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
    TClass2<float> myClass;
    WriteClassToConsole(myClass);
    std::getchar();
    return 0;
}

Thanks to everyone who helped.

Steztric
  • 2,832
  • 2
  • 24
  • 43