3
votes

How can I specialize a templatized function which is defined as a pure function in the base class?

struct A {
    virtual void func(int a) = 0;
    //virtual void func(int a) {} // replace above line with this and it works
};

struct B : public A {
    template<typename T> void func(T t) {
        cout <<"hello"<<endl;
    }
};
template<> void B::func<int>(int a) { cout <<"hello 2"<<endl; }


int main() {
    B b;
    b.func(2);
}

Error:

error: variable type 'B' is an abstract class B b; ^ note: unimplemented pure virtual method 'func' in 'B' virtual void func(int a) = 0;

3
You can't mix template and virtual functions.NathanOliver
I believe this is not a duplicate, since the override is template specialized. And also note that I have issues only with pure virtual function.Nujufas
@Nujufas the question is a bit different, but the answer is the same. Member function templates cannot be declared virtual.Stack Danny
@StackDanny acceptable, could you also explain why it works if the function is made 'not pure'?Nujufas

3 Answers

1
votes

//virtual void func(int a) {} // replace above line with this and it works

Replace the above line and the code compile, doesn't works.

Or, better, works but not as you expect.

The problem is that virtual functions and template functions doesn't mix very well.

So you can't make a template function that directly override a virtual function: if you define func() as a null virtual function

virtual void func(int a) = 0;

the base A class, and all derived classes, become not-instantiable until you don't define an effective virtual func() function.

Defining

virtual void func(int a) {}

the base A class, and all derivate, isn't not-instantiable anymore a you don't need anymore a redefinition of the virtual function.

But the template func() version is not related with virtual function.

When you call b.func(2) in main(), it's the template, not the virtual func() inherited from A, that is called. It's because the template func() "hide" the func() inherited virtual version.

You can "un-hide" the virtual func() version in B adding, in the body of B definition

using A::func;

This way, calling b.func(2); in main(), the virtual version inherited by A is called and the template specialization of func(), so the std::cout <<"hello 2" << std::endl; instruction, ins't executed anymore.

Now... if I understand correctly, you want a template func() function that, in case of T == int join the virtual specialization.

The only way I see is define the virtual override in B

void func (int a) override // override, so necessarily virtual
 { std::cout <<"hello 2" << std::endl; }  

and call it from the template specialization

template <>
void B::func<int> (int a)
 { func(a); } // call the virtual override version

The following is a full compiling example

#include <iostream>

struct A
 { virtual void func(int a) = 0; };

struct B : public A
 {
   void func (int a) override
    { std::cout <<"hello 2" << std::endl; }

   template<typename T>
   void func (T t)
    { std::cout << "hello" << std::endl; }
 };

template <>
void B::func<int> (int a)
 { func(a); }


int main ()
 {
   B{}.func(2); // call directly virtual func()
   B{}.func<int>(2); // call template func() specialization that call virtual func()
 }
5
votes

Virtual function could be overridden by only non-template functions. In this case,

Then this function in the class Derived is also virtual (whether or not the keyword virtual is used in its declaration) and overrides Base::vf (whether or not the word override is used in its declaration).

And note that function templates can't be virtual functions;

Functions templates cannot be declared virtual.

From the standard, [temp.mem]/4

A specialization of a member function template does not override a virtual function from a base class. [ Example:

class B {
  virtual void f(int);
};

class D : public B {
  template <class T> void f(T); // does not override B​::​f(int)
  void f(int i) { f<>(i); }     // overriding function that calls the template instantiation
};

— end example ]

About your question,

why it works if the function is made 'not pure'?

The compliation error disppears but it still doesn't work as you expected; the function template in the derived class doesn't override the virtual function of the base class. You can check it with dynamic dispatch:

If a derived class is handled using pointer or reference to the base class, a call to an overridden virtual function would invoke the behavior defined in the derived class.

Note that you should use pointer or reference to make dynamic dispatch works, e.g.

B b;
A* pa = &b;
pa->func(2);

LIVE

You can also apply override specifier to help you confirm overridding.

0
votes

This might be considered besides the point but there's a very nice mnemonic that you can use when designing with templates:

Virtual functions - dynamic polymorphism (solved at runtime through vtable)

Template specialization - static polymorphism (solved at compile time through type info)

Don't try to solve one with the other.

In your case you are attempting to provide a body to a virtual method (solve a runtime polymorphism) through template specialization.