Search code examples
c++type-erasurepure-virtual

Pure Virtual member function with compile-time known parameter?


I'm practicing Type Erasure Patterns by trying to implement one for STL containers and I'm stuck with on the pure virtual member functions of those containers. I do not know how to implement the "concept" of the type erasure pattern which acts as the interface, holding the pure virtual member functions shared by the erased types. Functions like Push will require a compile-time known parameter. As I understand, virtual functions cannot use auto or be templatized, so how can I go about writing the interface?

I tried using the keyword 'typename' to tell the compiler that the type will be given later, but it does not compile.

This is what I have so far for the 'concept' interface:

class Concept{
public:
    virtual void push(typename T val) = 0;
    virtual typename T pop() = 0;
};

The error received currently is as such:

error: expected nested-name-specifier before ‘T’ virtual void push(typename T val) = 0;
                              ^
error: expected ‘,’ or ‘...’ before ‘val’ virtual void push(typename T val) = 0;
                                ^~~
error: expected nested-name-specifier before ‘T’ virtual typename T pop() = 0;

If anyone can give me some advice regarding this, I'd really appreciate it. Thank you all in advance for your kind help and your time.


Solution

  • The typename keyword can only be part of a template declaration.

    template <typename T> class Concept{
    public:
        virtual void push(T val) = 0;
        virtual T pop() = 0;
    };
    

    You have mixed in your mind templates and pure virtual functions. The first is compile time, the second is run time.

    Templates allow you to avoid duplicating code for different data types, where pure virtual member functions allow you to use different polymorphic interfaces that inherit from the same type. Type erasure has also nothing to do with virtual member functions. Two completely different things.

    Once the above template is instantiated with, say, int, then it is equal to this:

       class Concept{
        public:
            virtual void push(int val) = 0;
            virtual int pop() = 0;
        };
    

    Now this class is abstract; you cannot instantiate it, but you can inherit:

    class f1 : public Concept {
        public:
        virtual void push(int val) { ... define it }
        virtual int pop()  { ... define it}
    };
    
    class f2 : public Concept {
        public:
        virtual void push(int val) { ... define it }
        virtual int pop()  { ... define it}
        ... more members
    };
    

    And use it polymorphically:

    Concept* a = new f1();
    Concept* b = new f2();
    
    // dynamic_cast<f1>(a) will return a f1*
    // dynamic_cast<f2>(b) will return a f2*