C++ : Vector of template class

2020-02-05 08:32发布

问题:

I have a template class named Cell as follows:-

template<class T>class Cell
{
    string header, T data;
}

Now I want another class Named Row. Row will have a vector named Cells such that I can add both Cell and Cell type elements to that vector. Is it possible?

If so, how can I do that? Thanks in advance.

回答1:

With the extra detail you've provided, the first two answers won't work. What you require is a type known as a variant for the cell and then you can have a vector of those. For example:-

enum CellType
{
  Int,
  Float,
  // etc
};

class Cell
{
  CellType type;
  union
  {
    int i;
    float f;
    // etc
  };
};

class Vector
{
  vector <Cell> cells;
};

This, however, is a pain to add new types to as it requires a lot of code to maintain. An alternative could use the cell template with a common base class:-

class ICell
{
  // list of cell methods
};

template <class T>
class Cell : public ICell
{
  T data;
  // implementation of cell methods
};

class Vector
{
  vector <ICell *> cells;
};

This might work better as you have less code initially to update to add a new cell type but you have to use a pointer type in the cells vector. If you stored the cell by value, vector <ICell>, then you will lose data due to object slicing.



回答2:

The reason why this is NOT possible in C++, but possible in Java/Python is because: in a C++ vector, the STL container's storage (returned by vector::data()) contains all the object instantiations sequencially packed. In which each element must have the same size. This makes addressing fast and convenient. Therefore, suppose you define a template class A,

template <class T>
class A{
  int id;
  T obj;
};

Its size will depend on the template variable "T obj". Pushing the same class A of different template type T will make each element in the vector having different sizes, thus, this is impossible. The only way is to use vector of shared_ptr or unique_ptr of a base class. Both shared_ptr and unique_ptr are supported by C++11 and Boost. Each derived-class element can have different template types. In this way, when the base class pointer's destructor is called, the derived class's destructor will be invoked. For example,

#include <memory>
#include <vector>
#include <iostream>
#include <string>

using namespace std;

class A{};

template <class T>
class AImpl : public A{
public:
    T obj;
    AImpl(T _obj):obj(_obj){}
    ~AImpl(){
        cout << "Deleting " << obj << endl;
    }
};

int main(int argc, char** argv)
{
    AImpl <string>* a1 = new AImpl <string> ("string1234");
    AImpl <int>* a2 = new AImpl <int> (1234);
    AImpl <double>* a3 = new AImpl <double> (1.234);
    vector <shared_ptr<A>> As;
    As.push_back(shared_ptr<A>(a1));
    As.push_back(shared_ptr<A>(a2));
    As.push_back(shared_ptr<A>(a3));
}

Remember to compile with -std=c++11 to enable C++11.

Output:

Deleting string1234
Deleting 1234
Deleting 1.234

And you get what you want! :)

In Java/Python, every class-object variable is actually a pointer, thus, a Java Array of A or a Python list of A is equivalent to a C++ array of pointers of A. Thus, you get essentially the same functionality without explicit creating shared_ptrs.



回答3:

The other answer is good, but you probably wanted:

template<class T>
class Row
{
private:
    class Cell {
        string header;
        T data;
    }

    std::vector<Cell> cells;
    ...
}


回答4:

Something like this?

template<class T>
class Row
{
private:
   std::vector<Cell<T> > cells;
};

Okay, this answer is incorrect.

So, if you want to store in one vector different cells - you should use some dynamic type identification (you can use one base-class and store pointer to it in vector, that use only virtual functions, that are overrided in all derived classes, you can store something like boost::any and save some type-identification for each inserted element, for cast them into real type and work with it).