Is an Abstract Base Class with Templates Possible?

Hi Everyone!

I am new to the forum and this is my very first question...

I've come across what I believe to be an intentional limitation of C++ and would like to know if there is a workaround.

I want to create an abstract base class having a member function that can accept a templatized structure as its parameter, something that according to C++'s rules can't be done for a good reason.

That good reason it is because an abstract base class is intended to provide interface rules to the classes that will derive from it and should not deal with data.

But how would you go about doing something like the following which is probably a reasonable design decision? And if it isn't, let me know...

template<typename T>
class Matrix { /* ... */ };

class iFile {
public:
virtual ~iFile() {} = 0;
virtual void Process(const Matrix<T>&) = 0;
virtual void Close() = 0;
}

class DSPFile: public iFile {
public:
DSPFile(const string& fname);
~DSPFile();
void Process(const Matrix<T>& data);
void Close();
}

Thanks!
Last edited on
It would make more sense in my mind to just template the method itself instead of the class since that method is the only things using it.

For example you iFile class might look like

1
2
3
4
5
6
7
8
class iFile
{
    public: 
      
        template <typename ParamType>
        virtual void Process(const Matrix<ParamType>&) = 0;
        ...
}


Though you of course would then need to pass the type whenever that method is called.
Last edited on
Although I didn't mention it, that is a strategy I already tried.

The error I get when I do this is "member function templates cannot be virtual".

I use Visual C++ Compiler November 2013 CTP, which is the latest.

I believe it's a C++ rather than a compiler issue.

PS. What do I need to enclose demo code in to make it appear like on my editor?
> "member function templates cannot be virtual"
> I believe it's a C++ rather than a compiler issue.

Yes.

Something like this is possible:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <typeinfo>
#include <functional>

struct matrix_base { virtual ~matrix_base() = default ; };

struct file_base
{
    virtual ~file_base() = default ;
    virtual void process( const matrix_base& ) = 0 ;
    // ...
};

void process( file_base& fb, const matrix_base& mb ) { fb.process(mb) ; }

template < typename T >  struct matrix : matrix_base { /* ... */ };

template < typename T > struct ifile : file_base
{
    virtual void process( const matrix_base& m ) override { process_(m) ; }

    private:
        void process_( const matrix_base& m ) // may throw std::bad_cast
        { return do_process( dynamic_cast< const matrix<T>& >(m) ) ; }

        virtual void do_process( const matrix<T>& m ) = 0 ;
};

template < typename T > struct DSPFile: public ifile<T>
{
    // ...

    private: virtual void do_process( const matrix<T>& m ) override
    {
        std::cout << typeid(*this).name() << "::do_process( const matrix<" << typeid(T).name() << ">& )\n" ;

        // ...
    };
};

int main()
{
    DSPFile<int> dspfi ;
    DSPFile<double> dspfd ;
    DSPFile<long> dspfl ;
    std::reference_wrapper<file_base> fb[] = {  dspfi, dspfd, dspfl } ;

    matrix<int> mi ;
    matrix<double> md ;
    matrix<long> ml ;
    std::reference_wrapper<matrix_base> mb[] = {  mi, md, ml } ;

    for( int i = 0 ; i < 3 ; ++i ) process( fb[i], mb[i] ) ;
}

http://rextester.com/ZOIH99066

If you could elaborate on your specific problem context, it can be customised to something that is more appropriate to the particular use-case.
What do I need to enclose demo code in to make it appear like on my editor?


You can use code tags to make your code appear correctly formatted here:

http://www.cplusplus.com/articles/z13hAqkS/
Something like
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Animal {
    public:
        template <class FOOD, int N>
        virtual void eat (const FOOD& food) const {
			std::cout << "I'm an animal and eat " << N << " times per day." << std::endl;
			// Do whatever with food
		}
        virtual ~Animal() = default;
};

class Wolf: public Animal {
    public:
        template <class FOOD, int N>
        virtual void eat (const FOOD& food) const override {
			std::cout << "I'm a wolf and eat " << N << " rabbits per day." <<  std::endl;
			// Do whatever with food
		}
};

won't compile because template functions cannot be virtual. So a Visitor Pattern solution can solve the problem:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <iostream>
#include <vector>
#include <typeinfo>

class Visitor {
	public:
		virtual void visit (class Animal*) const = 0;
		virtual void visit (class Wolf*) const = 0;
		virtual void visit (class Fish*) const = 0;
		virtual void visit (class GoldFish*) const = 0;
};

template <class FOOD, int N>
class FoodVisitor: public Visitor {
	private:
		FOOD food;
	public:
		FoodVisitor (const FOOD& _food): food (_food) {}
		virtual void visit (Animal*) const {  // Do whatever with food.  For example
			if (typeid (food) != typeid (GoldFish))
				std::cout << "I'm an animal and eat " << N << " times per day." << std::endl;
			else
				std::cout << "I even eat goldfish " << N << " times per day." << std::endl;
		}
		virtual void visit (Wolf*) const {
			if (typeid (food) != typeid (GoldFish))
				std::cout << "I'm a wolf and eat " << N << " rabbits per day." <<  std::endl;
			else
				std::cout << "But I don't eat goldfish because they are too tiny." << std::endl;
		}
		virtual void visit (Fish*) const {
			if (typeid (food) != typeid (GoldFish))
				std::cout << "I'm a fish and eat " << N << " worms per day." <<  std::endl;
			else
				std::cout << "I sometimes eat other fish like goldfish " << N << " times per day." << std::endl;
		}
		virtual void visit (GoldFish*) const {
			if (typeid (food) != typeid (GoldFish))
				std::cout << "I'm a goldfish and eat " << N << " teeny meals per day." <<  std::endl;
			else
				std::cout << "But I do not eat my own kind!" << std::endl;
		}
};

class Animal {
    public:
        virtual void eat (const Visitor& visitor) {  // the "accept" function of the Visitor Pattern
        	visitor.visit (this);
        }
        virtual ~Animal() = default;
};

class Wolf: public Animal {
    public:
        virtual void eat (const Visitor& visitor) override {  // The template is gone, but a FoodVisitor is a template class that derives from Visitor.
        	visitor.visit (this);
        }
};

class Fish: public Animal {
    public:
        virtual void eat (const Visitor& visitor) override {
        	visitor.visit (this);
        }
};

class GoldFish: public Fish {
    public:
        virtual void eat (const Visitor& visitor) override {
        	visitor.visit (this);
        }
};

int main() {
    std::vector<Animal*> animals = {new Animal, new Wolf, new Fish, new GoldFish};
    GoldFish goldFish;
    FoodVisitor<std::string, 4> foodVisitor1 ("insect");
    FoodVisitor<GoldFish, 2> foodVisitor2 (goldFish);
    for (Animal* x : animals)
    {
        x->eat(foodVisitor1);  // templates are used, but not for eat but for foodVisitor1
        x->eat(foodVisitor2);
    }
}

/*
Output:
I'm an animal and eat 4 times per day.
I even eat goldfish 2 times per day.
I'm a wolf and eat 4 rabbits per day.
But I don't eat goldfish because they are too tiny.
I'm a fish and eat 4 worms per day.
I sometimes eat other fish like goldfish 2 times per day.
I'm a goldfish and eat 4 teeny meals per day.
But I do not eat my own kind!
*/

In the above example, FOOD itself can be treated with the Visitor Pattern to treat all the ways that each animal reacts to each type of food.
JLBorges is the person who taught me the Visitor Pattern, by the way.
Last edited on
Topic archived. No new replies allowed.