Is it possible to use PolyMorphism when using Templates?
For example, I have a class called “Filters” and I have many different variations / classes of how the data can be filtered and therefore I initialise an object based on the Template (which type of filter is defined in main)
#include "Filter1.h"
#include "Filter2.h"
template<typename T>
class Filters {
public:
void Filter(vector<double> &vec) {
T type;
type.Filter(vec);
}
};
// class Filter1
class Filter1 {
public:
void Filter(vector<double> &vec) {
// Code for "Filter1"
}
};
// MAIN
int main() {
vector<double> sample; // this is a sample vector
Filters<Filter1> exam1;
exam1.filter(sample);
}
This would work, however, in “Filter2” let’s say we have more paramaters we pass:
class Filter2 {
public:
void Filter(vector<double> &vec, double point)
{
// Filter 2
}
};
And then the main:
int main()
{
vector<double> sample;
double point = 9;
Filters<Filter2> exam;
exam.Filter(sample, point);
}
This would not work, because, “Filter” in “Filters” only accepts 1 paramater.
The problem that I am having is the fact that the Filters differer in the paramaters they accept. For example “Filter1” passes through a 2D vector, and a double but the Filter method definition in this class only accepts a 1D vector.
I was thinking (Theoretically) I could have a switch statement (“T”) that offers a initialises the different classes.
Any ideas would be appreciated .
When you do generic programming with templates, you need to code against interfaces. I’m not using the OOP meaning of the term here — the wider meaning instead.
As an example, here is a function template that codes against the Random-Access Iterator concept-like interface:
(This example is of course bogus, the intent here is to show multiple Random-Access Iterator operations.)
Because we specify in our contract that
Itis a Random-Access Iterator, we knowsumhas access to:std::iterator_traits<It>::value_typeandstd::iterator_traits<It>::difference_type, which are types that can be initialized from the result ofoperator[]andoperator-respectivelyItlikeoperator-andoperator[], which are not available with e.g. Bidirectional IteratorsAs such
sumcan be used withstd::vector<int>::iterator,std::deque<double>::const_iteratorandlong*, which are all different types which may differ in some aspects but at least are all models of the Random-Access Iterator concepts.(The observant will have noticed that by using
0and+=we in turn require in our contract that thevalue_typebe an arithmetic-like type. That’s again an interface we code against!)Then when designing your
Filterswhich you apparently intend to use asFilters<FilterLike>, you need to ask yourself what is the common minimal interface that aFilterLikeneeds to fulfill. If there is someFilterXwhich almost is aFilterLikeexcept perhaps for some operation, here are some options:as you mention in your question, wherever
Filtersuses that particular operation you can special-case it so thatFilterXis specially handled — this is the probably the worst thing you can do. It’s brittle in that you have to do it at every site where you need the operation (even if it looks like it’s only one right now, what about in the future?); it’s inconvenient in that no, you can’t switch on a type inside a function body of a class template function member (you have to use different techniques which are verbose and unobvious); and it introduces coupling in thatFiltershas to know aboutFilterX— why should it care?write an explicit specialization for
Filters<FilterX>. This is much like the above, but is an interesting option whenFilterXdiffer not in just one or two operations. Unlike the previous solution, this isn’t as brittle because the primary template is left untouched and all theFilterXspecific stuff is put in the same spot. If on the other hand half ofFilterXalready behave like aFilter, then that must mean there is either half the code ofFilters<FilterLike>that ends duplicated or some extra work is needed to refactor out the common code betweenFilters<Filter>andFilters<FilterX>. Hence the amount of coupling varies — if the primary template doesn’t have to know about this explicit specialization then it’s a good option, and you don’t even have to bundle the explicit specialization with the primary templatewrite an
AdaptedFilterXthat is a model of theFilterLikeinterface and forwards all its operation to an underlyingFilterX. If you have severalFilterX,FilterYthat are all almost models ofFilterbut have a common interface, you can write anAdaptedFilter<FilterX>— in a sense theAdaptedFiltertemplate ‘transforms’ a model of aFilterXLikeinto a model of aFilterLikeAs an aside, if you were using C++11 you could have written
Filterto accept arbitrary arguments to construct aFilterLikewith:It’s perhaps simpler (and works for C++03) to just accept a
FilterLikethough: