简体   繁体   中英

C++ templates: Calling member function of derived template class from base class

I'm currently working on a spreadsheet application, but I'm having problems with templates. Every cell of the template can contain a variable which can be any of the standard types.

The relevant class is SpreadSheet , whose most important member variable is SheetCells , which has type vector< vector<CellBase*> > . The CellBase class is an abstract class from which CellField<T> is derived, the latter being the template class storing one piece of data corresponding to exactly one cell of the spreadsheet.

I have another class, SheetView , that eventually has to display the spreadsheet. (To keep things simple, assume that this class has full access to every other class.) This class doesn't really care what type the value of every cell is, as it will convert everything to a string anyway. However, my problem is writing a member function of SpreadSheet that returns a string, containing the data. My first idea was to write a function std::string SpreadSheet::getDataFromSheet(int row, int column) that SheetView would call, and then that function would do return (std::to_string(SheetCells[row][column] -> getData())) , where getData() is a member function of CellField<T> , returing something of type T . However, since SheetCells contains pointers to CellBase classes, I must make getData a member of CellBase , but this is not possible, since I want getData() to return a variable of type T , the same type as the template class CellField .

The relevant parts of all class definitions are found below.

//SpreadSheet

class Spreadsheet
{

private:
    int _height, _width;

public:
    Spreadsheet(int newHeight, int newWidth);
    ~Spreadsheet();
    string getData(int row, int column);
    vector< vector<CellBase*> > SheetCells;

};



//CellBase

class CellBase
{
    public:
        CellBase();
        virtual ~CellBase();
};



//CellField

template<typename T>
class CellField : public CellBase
{
    public:
        CellField(T newValue);
        virtual ~CellField();
        T getData();
        T _value;
};

So in short, I want to be able to call getData() from SpreadSheet , but the member variables of the latter only contain pointers to CellBase classes (but the classes are actually of type CellField<T> ).

I've looked at similar questions, but none of them seem to address the issue of a base class member function calling a template class<T> function where the latter and the former need to return a variable of type T . Maybe void* pointers will work?

As C++ is a strongly typed language, you can't call them directly this way, since the compiler would not be able to figure out what the return value of the function is.

What you need to do is, map it all to a common interface. The question you should ask is: what are the information i realy need from the CelField? Maybe all you need is the string-representation of the value, then you could do something like this:

class CellBase
{
    virtual std::string getData()=0;
};

template<typename T>
class CellField : public CellBase
{
    std::string getData(){//some implementation}
};

Another option is the use of boost::any , which is able to contain any type you like. This is especially useful if you don't need to actually interfere with the returned value other than passing it to some other function taking "an arbitrary parameter". However in order to really use the value, you still have to cast it to a specific type using boost::any_cast<T>() and therefore need to know which type you expect and do a proper error-handling if the type is wrong.

A possible solution is employ a visitor, along these line:

Class Visitor
{
   virtual ~Visitor(void) {}
   virtual void visit(CellBase<int> *cell) {}
   virtual void visit(CellBase<float> *cell) {}
...
} ;

class CellBase
{
public:
     CellBase();
     virtual ~CellBase();
     virtual void accept(Visitor *v) { v->visit(this) ;}
};

class DataGetterVisitor : public Visitor
{
public:
    virtual void visit(CellBase<int> *cell) 
    {
       // here I know how to make the transformation

    }
    virtual void visit(CellBase<float> *cell) {}
    string text ;
} ;

string dataGetter(CellBase *cell)
{
    DataGetterVisitor visitor ;
    cell->accept(visitor);
    return visitor.text ;
}

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