简体   繁体   中英

non templated interfaces to templated classes c++

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?

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).

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).

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM