2

I am trying to call a templated function from within an overridden virtual function of a class. Unfortunately, I can only seem to call the default implementation.

#include <iostream>
#include <string>
#include <typeinfo>
#include <memory>

template<typename T1,
         typename T2>
class B;

template<typename T1,
         typename T2>
void bar(B<T1, T2> &b)
{
    std::cout << "T1 = " << typeid(T1).name() << std::endl;
    std::cout << "T2 = " << typeid(T2).name() << std::endl;
    std::cout << "Default template" << std::endl;    
}

template<typename T2>
void bar(B<int, T2> &b)
{
    std::cout << "Specialized template" << std::endl;    
}

template<typename T1, typename T2>
void foobar(T1 t1, T2 t2)
{
    std::cout << "Default template [foobar]" << std::endl;
}

template<typename T2>
void foobar(int t1, T2 t2)
{
    std::cout << "Specialized template [foobar]" << std::endl;
}

class A 
{
public:
    A() {}
    ~A() {}

    virtual void foo() = 0;
};

template<typename T1,
         typename T2>
class B : public A 
{
public:
    B() {}
    ~B() {}

    void foo() override
    {
        std::cout << "T1 = " << typeid(T1).name() << std::endl;
        std::cout << "T2 = " << typeid(T2).name() << std::endl;
        bar<T1, T2>(*this);
    }
};

int main()
{
    auto a = new B<int, char>();
    a->foo();

    bar<int, char>(*a);

    int i = 0;
    char b = 'b';
    foobar(i, b);
    foobar(b, b);

    return 0;
}

And the output is (see: here)

T1 = i
T2 = c
T1 = i
T2 = c
Default template
T1 = i
T2 = c
Default template
Specialized template [foobar]
Default template [foobar]

Clearly the overridden foobar function works fine, however, for the bar function, the specialised version is never called. The outputs in the console show that the first template parameter is clearly an int and so the overridden bar function should be called.

I have a feeling this is related to the virtual function foo being incompatible with the class template [1]. In principle I would like to generate different versions of the B class that will override the virtual foo function and call upon the appropriate specialisation of bar (depending on the class template parameters).

I am aware that I can just have partial specialisations of the B class however this leads to a lot of duplicate code and other complications with my design. (I am also aware that some of this can alleviated through templated inheritance).

I am mainly just curious as to why the correct version of bar is not called despite the template parameters clearly being correct. Rather than just a suggestion for a complete redesign, I would like to know why there is a conflict.

Roy
  • 3,027
  • 4
  • 29
  • 43

1 Answers1

2

The problem here is that bar are actually two separate template function overloads with first having priority. To define partial specialization for a function template you can to use a trick with proxy template class doing all the job:

template<typename T1, typename T2> class
bar_impl
{
    public: static void
    Do(B<T1, T2> &b)
    {
        std::cout << "T1 = " << typeid(T1).name() << std::endl;
        std::cout << "T2 = " << typeid(T2).name() << std::endl;
        std::cout << "Default template" << std::endl;    
    }
};

template<typename T2> class
bar_impl<int, T2>
{
    public: static void
    Do(B<int, T2> &b)
    {
        std::cout << "Specialized template" << std::endl;   
    }
};

template<typename T1, typename T2> void
bar(B<T1, T2> &b)
{
    return bar_impl<T1, T2>::Do(b);
}

online compiler

user7860670
  • 35,849
  • 4
  • 58
  • 84
  • That is exactly what I'm looking for. I should be able to add multiple specialised functions inside the bar_impl classes, this is great. Thanks. – Roy Jun 30 '18 at 22:25