Categories
Code

Mixing Inheritance and Templates in C++

I came across a gotcha today with some C++ code I’m working on. I have an abstract base class for doing interpolation, and when I tried converting it to a template class it stopped working – specifically the derived class could no longer access certain members of the base class.

Here’s my abstract base class (simplified for this example):

class Interpolation
{
public:
	Interpolation(const string& name, const double& accuracy)
	{
		_name = name;
		_accuracy = accuracy;
		_isBuilt = false;
	}
	virtual void Init(void) = 0;
	virtual void LoadFromFile(const string& filename) = 0;
	virtual void SaveToFile(const string& filename) = 0;
protected:
	string _name;
	double _accuracy;
	bool _isBuilt;
};

I then have a layer of inheritance on top of that, defining the interface for interpolation in two variables

class Interpolation2D : public Interpolation
{
public:
	Interpolation2D(const string& name, const double& accuracy, 
		const long& Nx, const long& Ny, 
		const double& xmin, const double& xmax, 
		const double& ymin, const double& ymax)
		: Interpolation (name, accuracy)
	{
		
		_grid = new Data2D<double>(Nx, Ny);
		...
	}
	~Interpolation(void)
	{
		cout << "deleting data grid for " << _name << "\n";
		delete _grid;
	}
	// these are implemented here for two-D interpolations
	virtual void SaveToFile(const string& filename);   
	virtual void LoadFromFile(const string& filename);
protected:
	Data2D<double>* _grid; 
};

This code was working fine. I have the templated 2D data array class Data2D and chose to use doubles for my interpolation data. The inherited _name and _isBuilt variables work fine, as in the debug output line in the destructor.NB: I do implement the SaveToFile and LoadFromFile functions here, which basically just dump the _grid variable to a binary file.

Then I discovered the rather obvious fact that if I want to make a very fine-grained interpolation, that can take a lot of memory. For example, a 5000×5000 grid of doubles is 5000x5000x8 bytes, i.e. 190MB. Too rich for my laptop at least, although not absurd for most of the machines I use for simulation.

One obvious choice would be to use float instead of double as the data type. Of course, this leads to wanting to template the Interpolation class, which I duly did:

template <class T>
class Interpolation
{
public:
	Interpolation(const string& name, const T& accuracy)
	{
		_name = name;
		_isBuilt = false;
		_accuracy = accuracy
	}
	virtual void Init(void) = 0;
	virtual void LoadFromFile(const string& filename) = 0;
	virtual void SaveToFile(const string& filename) = 0;
protected:
	string _name;
	bool _isBuilt;
	T _accuracy;
};

template <class T>
class Interpolation2D : public Interpolation<T>
{
public:
	Interpolation2D(const string& name, const T& accuracy, 
		const long& Nx, const long& Ny, 
		const T& xmin, const T& xmax, 
		const T& ymin, const T& ymax)
		: Interpolation<T> (name)
	{
		
		_grid = new Data2D<T>(Nx, Ny);
		...
	}
	~Interpolation(void)
	{
		cout << "deleting data grid for " << _name << "\n";
		delete _grid;
	}
	virtual void SaveToFile(const string& filename); 
	virtual void LoadFromFile(const string& filename);
protected:
	Data2D<T>* _grid; 
};

So, imagine my surprise and confusion when this code no longer works. Specifically, gcc complains about the debugging output in the derived class’ destructor.

./include/aon_interpolation.h:179: error: "_name" was not declared in this scope

The problem, according to the C++ FAQ, is that the variable _name is not dependent on the template, and the compile will not look in the base template class to resolve non-dependent names.

Luckily there’s a really easy fix: change the call to use

this->_name

instead: i.e.

cout << data grid for " << this->_name << "\n";

C++ Is just one of those things that constantly surprises me with weird little things like this, even after 7 years of using it. But it’s still so powerful it’s mostly a pleasure to use.