4

Why won't the compiler select the Interface template when running the following code? Are additional declarations / hints needed or won't this work in general?

I'm just curious if this is actually possible.

class Interface {
    public :
       virtual void Method() = 0;
       virtual ~Interface() { }
};

class Derived : Interface {
    public : 
       void Method() {
            cout<<"Interface method"<<endl;
       }
};

template<typename T> 
struct Selector {
    static void Select(T& o) {
        cout<<"Generic method"<<endl;
    }
};

template<> 
struct Selector<Interface> {
    static void Select(Interface& o) {
        o.Method();
    }
};

int i;
Selector<int>::Select(i)       // prints out "Generic method" -> ok
Derived d;
Selector<Derived>::Select(d);  // prints out "Generic method" -> wrong
                               // should be "Interface method"
andwffn
  • 43
  • 3

3 Answers3

6

Try this (and #include <type_traits>):

template <typename T, typename = void>
struct Selector
{
    static void Select(T & o)
    {
        std::cout << "Generic method" << std::endl;
    }
};

template <typename T>
struct Selector<T,
           typename std::enable_if<std::is_base_of<Interface, T>::value>::type>
{
    static void Select(Interface & o)
    {
        o.Method();
    }
};

It turns out that enable_if combined with defaulted template arguments can be used to guide partial specialisations.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
5

The compiler will select the version of a function that is the closest match. A function that takes the exact type for a parameter always wins over one that requires a conversion. In this case the template function is an exact match, since it matches anything; the Interface specialization would require the conversion of the parameter from a Derived to an Interface.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
0

This will allow you to achieve the desired result:

#include <iostream>
#include <type_traits>
using namespace std;

class Interface {
    public :
       virtual void Method() = 0;
       virtual ~Interface() { }
};

class Derived : public Interface {
    public : 
       void Method() {
            cout<<"Interface method"<<endl;
       }
};

template<typename T, typename S = void> 
struct Selector {
    static void Select(T& o) {
        cout<<"Generic method"<<endl;
    }
};

template<typename T>
struct Selector<T, typename enable_if< is_base_of<Interface, T>::value >::type> {
    static void Select(Interface& o) {
        o.Method();
    }
};

int main()
{
int i;
Selector<int>::Select(i);       // prints out "Generic method" -> ok
Derived d;
Selector<Derived>::Select(d);  // prints out "Generic method" -> wrong
                               // should be "Interface method"
}
queen3
  • 15,333
  • 8
  • 64
  • 119