Chiel
Chiel

Reputation: 6194

Create template class which takes template class as argument in a constructor

I have a code which consists of three layers. At the highest level there is a Master class. Then there is a Grid class, that knows the Master, since it uses its functions for general tasks. On a Grid, I can create Fields, which take the Grid as an argument in its constructor. Now, I would like to make both Grid and Field templates, to enable them to contain arbitrary data types.

I don't manage to let Field handle the type that has been given to Grid correctly. In principle he should be able to deduce it. How do I make this work? My example, of course, does not compile yet.

#include <vector>

class Master
{
  public:
    Master() {};
};

template<class T>
class Grid
{
  public:
    Grid(Master &masterin) : master(masterin) {};
  private:
    Master &master;
    std::vector<T> data;
};

template<class T>
class Field
{
  public:
    Field(Master &masterin, Grid &gridin) : master(masterin), grid(gridin) {};
  private:
    Master &master;
    Grid   &grid;
    std::vector<T> data;
};

int main()
{
  Master master;
  Grid<int> grid(master);
  Field<double> field(master, grid);

  return 0;
}

Upvotes: 0

Views: 149

Answers (2)

Jonathan Wakely
Jonathan Wakely

Reputation: 171433

template<class T>
class Field
{
  public:
    Field(Master &, Grid &);

This is not valid, you cannot pass an argument of type Grid because Grid is not a type

  private:
    Master &master;
    Grid   &grid;

You cannot have a member of type Grid because it is not a type

You can fix the constructor by making it a template:

template<typename U>
    Field(Master &, Grid<U> &);

This will accept any type that is a specialization of the Grid template, but you still can't have a member variable of a template, you need to decide what kind of Grid you want the member to be, or store it in a flexible type like boost::any.

If you want Field to be able to work with any kind of Grid then it needs another template parameter:

template<class T, class U>
class Field
{
  public:
    Field(Master&, Grid<U>&);
  private:
    Master&  master;
    Grid<U>& grid;
    std::vector<T> data;
};

To deduce the type of Grid<U> you can define a generator function:

template<typename T, typename U>
  Field<T, U>
  make_field(Master& m, Grid<U>& g)
  { return Field<T, U>(m, g); }

Master master;
Grid<int> grid;
auto field = make_field<double>(master, grid);  // returns Field<double, int>

However, without C++11's auto keyword that is inconvenient to use and doesn't really help much as you have to spell out the full type anyway:

Field<double, int> field = make_field<double>(master, grid);

Upvotes: 3

Puppy
Puppy

Reputation: 147036

Grid is a template, so it must have a template argument to be useful here. Given your example, it seems that you want Grid and Field to have independent types. This means that Field must be templatized with two parameters- one for itself and one for the Grid it works with.

Upvotes: 1

Related Questions