4

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;

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Nujufas
  • 676
  • 1
  • 9
  • 19
  • 1
    You can't mix template and virtual functions. – NathanOliver Jul 09 '19 at 14:03
  • Possible duplicate of [Can a class member function template be virtual?](https://stackoverflow.com/questions/2354210/can-a-class-member-function-template-be-virtual) – Stack Danny Jul 09 '19 at 14:03
  • 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 Jul 09 '19 at 14:12
  • @Nujufas the question is a bit different, but the answer is the same. `Member function templates cannot be declared virtual.` – Stack Danny Jul 09 '19 at 14:15
  • @StackDanny acceptable, could you also explain why it works if the function is made 'not pure'? – Nujufas Jul 09 '19 at 14:19
  • @Nujufas it works because at that moment there are two different `func`'s in `B`. one from `A`: `A::func(int);` and one from `B`: `B::func(T);`. This forces `A` to provide implementation and therefore removes its status as an abstract class. – Stack Danny Jul 09 '19 at 14:26

3 Answers3

5

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.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • I'm not declaring the functions template virtual. – Nujufas Jul 09 '19 at 14:04
  • 2
    You can only override with a virtual functions. Overrides are implicitly virtual. Try marking your function as `override` – Guillaume Racicot Jul 09 '19 at 14:05
  • 1
    @GuillaumeRacicot that still won't work if the function that overrides is templated. – Stack Danny Jul 09 '19 at 14:10
  • 2
    @StackDanny Pertty sure they are saying mark it `override` so they can get a compiler error saying templates can't be used. – NathanOliver Jul 09 '19 at 14:11
  • 1
    The quoted example from the Standard shows exactly how to make the derived class act like the template does override the virtual function, if that's what you want: define an exact override which just calls the template. In the question's example, this looks like `void func(int a) override { func<>(a); }` in the definition of `B`. – aschepler Jul 09 '19 at 18:45
2

//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()
 }
max66
  • 65,235
  • 10
  • 71
  • 111
0

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.

MichaelCMS
  • 4,703
  • 2
  • 23
  • 29