简体   繁体   中英

Using Polymorphism with Templates

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 .

if you are depending on templates another way would be to pass filter that template as argument. Mostly filter are dependent on standart operations like + - /. Therefore you can overload that functions in your Vector2D, Vector1D ... classes and your filter function will call that methods automatically.

Hope this helps a bit :)

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:

template<typename It>
typename std::iterator_traits<It>::value_type
sum(It first, It last)
{
    typedef typename std::iterator_traits<It>::difference_type diff_t;
    diff_t const size = last - first;

    typename std::iterator_traits<It>::value_type accum = 0;
    for(diff_t i = 0; i != size; ++i) {
        accum += first[i];
    }

    return accum;
}

(This example is of course bogus, the intent here is to show multiple Random-Access Iterator operations.)

Because we specify in our contract that It is a Random-Access Iterator, we know sum has access to:

  • member-types std::iterator_traits<It>::value_type and std::iterator_traits<It>::difference_type , which are types that can be initialized from the result of operator[] and operator- respectively
  • operations on It like operator- and operator[] , which are not available with eg Bidirectional Iterators

As such sum can be used with std::vector<int>::iterator , std::deque<double>::const_iterator and long* , 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 0 and += we in turn require in our contract that the value_type be an arithmetic-like type. That's again an interface we code against!)

Then when designing your Filters which you apparently intend to use as Filters<FilterLike> , you need to ask yourself what is the common minimal interface that a FilterLike needs to fulfill. If there is some FilterX which almost is a FilterLike except perhaps for some operation, here are some options:

  • as you mention in your question, wherever Filters uses that particular operation you can special-case it so that FilterX is 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 that Filters has to know about FilterX -- why should it care?

  • write an explicit specialization for Filters<FilterX> . This is much like the above, but is an interesting option when FilterX differ 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 the FilterX specific stuff is put in the same spot. If on the other hand half of FilterX already behave like a Filter , then that must mean there is either half the code of Filters<FilterLike> that ends duplicated or some extra work is needed to refactor out the common code between Filters<Filter> and Filters<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 template

  • write an AdaptedFilterX that is a model of the FilterLike interface and forwards all its operation to an underlying FilterX . If you have several FilterX , FilterY that are all almost models of Filter but have a common interface, you can write an AdaptedFilter<FilterX> -- in a sense the AdaptedFilter template 'transforms' a model of a FilterXLike into a model of a FilterLike

As an aside, if you were using C++11 you could have written Filter to accept arbitrary arguments to construct a FilterLike with:

template<typename... Args>
void Filter(Args&&... args)
{
    FilterLike filter(std::forward<Args>(args)...);
    // go on using filter...
}

It's perhaps simpler (and works for C++03) to just accept a FilterLike though:

void Filter(FilterLike filter)
{
   // go on using filter...
}

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