0

I have a function template that I have specialized for a specific type. I'm having trouble getting the specialized version to be called in certain circumstances. To illustrate

struct Base {};
struct Derived : public Base {};


template <typename VALTYPE> void FooInternal(VALTYPE&) 
{
    std::cout << L"FooInternal template";
}

template<> void FooInternal(Base&) 
{
    std::cout << L"FooInternal SPECIAL"; 
}

Now if I construct an instance of "Base" or "Derived" and call "FooInternal", all works as I would expect

int _tmain(int argc, _TCHAR* argv[])
{
    int x = 7;
    FooInternal(x);  // Calls FooInternal<VALTYPE>() template

    Base b;
    FooIntenral(b);  // Calls FooInternal<Base>() specialization

    Derived d;
    FooInternal(d);  // Calls FooInternal<Base>() specialization

    return 0;
}

The output of this is

FooInternal template
FooInternal SPECIAL
FooInternal SPECIAL

}

But suppose I have an intermediate function template between these two that calls FooInternal. In this case, the template resolution for the derived type seems to fail along the way

// Intermediate template.  Just calls FooInternal.

template<typename VALTYPE>
void Foo(VALTYPE& val)
{
    FooInternal<VALTYPE>(val);
}


// Now repeat the same 3 calls and see what happens with Derived...

int _tmain(int argc, _TCHAR* argv[])
{
    int x = 7;
    Foo(x);  // Calls FooInternal<VALTYPE>() template

    Base b;
    Foo(b);  // Calls FooInternal<Base>() specialization

    Derived d;
    Foo(d);  // Calls FooInternal<VALTYPE>() template!!!

    return 0;
}

The output of this program is

FooInternal template
FooInternal SPECIAL
FooInternal template

I can't understand why -- in the 3rd call "Foo" will not then call the specialized version of FooInternal as it did when the call was direct. Shouldn't the compiler understand that is derived from 'Base' in this case? What rule am I missing here?

I am using Microsoft Visual Studio 2012 Update 3, if that matters.

-Joe

dyp
  • 38,334
  • 13
  • 112
  • 177
user2057722
  • 51
  • 1
  • 3
  • With the [c++] tag, you'll get more attention and **syntax highlighting**. – dyp Jul 18 '13 at 22:01
  • You have an explicit (full) specialization for `Base&` whereas the type you use to in the explicit specification used to call `FooInternal` it is `Derived`, not `Base`. – dyp Jul 18 '13 at 22:06
  • I realize that I use a Derived in that call. But that does not explain why it works when I call FooInternal directly with Derived. Shouldn't the call that Foo makes end up causing the compiler to make the same resolution it did when I called Foo directly? – user2057722 Jul 18 '13 at 22:22
  • Well, the template argument `VALTYPE` in either case is `Derived`. And in the definition of `Foo`, `FooInternal(val)` says to use the same type again. There is no template argument deduction there since you provided the template argument. – aschepler Jul 18 '13 at 23:07

1 Answers1

1

Your expectation in the first example, and apparently also your compiler, are wrong. The output should be "FooInternal templateFooInternal SPECIALFooInternal template".

A function template specialization does not do anything at all to template argument deduction or overload resolution. It only gets used if rules without looking at it happen to end up with the exact same template arguments.

Most of the time, when you think you want a function template specialization, it would be a better idea to overload the function instead (with another template or with a non-template).

inline void FooInternal(Base&) 
{
    std::cout << L"FooInternal SPECIAL"; 
}

And then of course that FooInternal can never be called if you specify template arguments, so you want:

// Intermediate template.  Just calls FooInternal.

template<typename VALTYPE>
void Foo(VALTYPE& val)
{
    FooInternal(val);
}

This should get you what you were looking for (on all compilers).

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • If I were to take this function and turn it into a class, (e.g. function object) should the compiler -- according to C++ rules -- be able to deduce the arguments and call the functions as I was trying to do here? Or am I faced with the same problem? Would I need std::enable_if and std::is_base_of? – user2057722 Jul 18 '13 at 22:33
  • @user2057722 IIRC the only point where a type is deduced for class templates is *partial specialization*. There is no deduction for explicit (complete) specializations. You can use SFINAE via `enable_if` and `is_base_of` to select a *partial specialization* (for a class template), this might "solve" your problem (though I agree with aschepler that overloading the function template is easier). – dyp Jul 18 '13 at 23:04
  • I was hoping to have a single function name to handle all my types. The real-life code is reading data from an HDF5 file. Most types I read POD types (int, float, short, etc) handled the same way. A template is perfect for that. I don't want to have write separate overloads for all POD types. But for strings, I want to be able to pass a std::string& and have some special handling -- without calling a separate function. I was hoping that specializing a template would accomplish both tasks. i.e. Give me the boiler-plate for the POD types and the one special overload for the string. – user2057722 Jul 22 '13 at 16:23