4

The following code prints:

generic
overload

But what I wanted is that the overload or the specialization were called in both cases, not the generic one. I'm not trying to mix overloading with template specialization, they're here together because none worked as I expected. Is there any template magic to accomplish this?

#include <iostream>

class Interface {};
class Impl: public Interface {};

class Bar
{
public:
    template<typename T> void foo(T& t) {
        std::cout << "generic\n";
    }
    void foo(Interface& t) {
        std::cout << "overload\n";
    }
};
template<> void Bar::foo<Interface>(Interface& t) {
    std::cout << "specialization\n";
}

int main() {
    Bar bar;
    Impl impl;
    Interface& interface = impl;
    bar.foo(impl);
    bar.foo(interface);
    return 0;
}
GogaRieger
  • 345
  • 4
  • 10

3 Answers3

5

Two ways using type_traits to test if the argument is derived from Interface.

#include <boost/type_traits.hpp>

class Interface {};
class Impl: public Interface {};

class Bar
{
    template <class T> void foo_impl(T& value, boost::false_type)
    {
        std::cout << "generic\n";
    }
    void foo_impl(Interface& value, boost::true_type)
    {
        std::cout << "Interface\n";
    }
public:
    template<typename T> void foo(T& t) {
        foo_impl(t, boost::is_base_of<Interface, T>());
    }

};

Or disable the template if the condition is met, leaving only the non-template as a candidate.

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>

class Interface {};
class Impl: public Interface {};

class Bar
{
public:
    template<typename T>
    typename boost::disable_if<boost::is_base_of<Interface, T>, void>::type foo(T& t)
    {
        std::cout << "generic\n";
    }

    void foo(Interface&)
    {
        std::cout << "Interface\n";
    }
};
UncleBens
  • 40,819
  • 6
  • 57
  • 90
2

In order to use the specialized function, the compiler would need to do a parameter conversion from &Impl to &Interface. When it's looking for a function signature match, exact matches are preferred over ones that require a conversion. Since the generic foo<T> is an exact match, it wins out over both the overload and the specialized function.

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

The template definition allows a function to be created:

void Bar::foo<Impl>(Impl& t)

which is a better match than the ones you defined which take an Interface& parameter.

You have to make the superclass function a better match, possibly like this:

class Bar
{
    struct fallback { fallback(int) {} };
    template<typename T> void foo(T& t, fallback) {
        std::cout << "generic\n";
    }
    void foo(Interface& t, int) {
        std::cout << "overload\n";
    }
public:
    template<typename T> void foo(T& t) {
        foo(t, 0);
    }
};

Doesn't seem to actually work though, see http://ideone.com/IpBAv

So you'd need a type test inside the generic version, looking for subclasses of Interface.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • When I first looked at that code, I thought it's ill-formed for the same reasons GCC thinks it's ill-formed. But on a second thought, I think that this should resolve to the non-template, because non-template functions are a better match than function template specializations if everything else is equal – Johannes Schaub - litb Dec 06 '10 at 23:11
  • @Johannes: Do you know a solution? – Ben Voigt Dec 06 '10 at 23:12
  • No, please disregard my above comment: It's not true because "if everything else is equal" should say "if the non-template has no worse conversion for any of its parameters, and the template has no better conversion for any of its parameters". So as my very first comment said, this *is* a criss cross: http://stackoverflow.com/questions/3519282/why-is-this-ambiguity-here/3525172#3525172 , and GCC is correct. – Johannes Schaub - litb Dec 06 '10 at 23:15
  • I tried http://ideone.com/ZRHfa, but I think it's a non-inferable context so the template no longer works at all. – Ben Voigt Dec 06 '10 at 23:33