Re: Why is a template method instantiated and preferred over method
with matching signature?
On Jan 4, 4:45 pm, Matthias <matthias.schweinochNOS...@gmx.de> wrote:
I'm having a little issue with the compiler trying to
instantiate template code for a type even if there is an
explicit match for that type.
The fact that there may be an exact match elsewhere does not
inhibit template instantiation. The template still gets
instantiated and considered in overload resolution. If all
other things are equal, a non-template function will be
preferred over the instantiation of a template, but that's a big
if.
Here's some example code that compiles and runs on g++ 4.4.1:
//// code start
#include <iostream>
class IBase
{
public:
virtual ~IBase() {}
};
class Derived : public IBase {};
class FooCaller
{
public:
void foo(int value)
{
std::cout << "foo(int)\n";
}
void foo(double value)
{
std::cout << "foo(double)\n";
}
// void foo(const IBase * p)
void foo(IBase * p)
{
std::cout << "foo(IBase*)\n";
}
template<typename T>
void foo(const T & t)
{
std::cout << "foo(T)\n";
}
};
int main(int argc,char ** argv)
{
int iVal = 0;
double dVal = 0;
Derived derived;
FooCaller f;
f.foo(iVal);
f.foo(dVal);
f.foo(derived);
IBase * pValue = &derived;
f.foo(pValue);
return 0;
}
//// code end
Compiling and running this program produces the following
output:
foo(int)
foo(double)
foo(T)
foo(IBase*)
This is actually what I'm looking for. However, I do not want
to pass the IBase pointer as a non-const. If I replace the
plain pointer declaration with the const pointer declaration,
i.e.
FooCaller::foo(IBase*) -> FooCaller::foo(const IBase*)
then recompiling and running the program produces the
following output:
foo(int)
foo(double)
foo(T)
foo(T)
Even though I have a method foo(const IBase*), the compiler
instantiates a method FooCaller::foo(const IBase* &) instead,
which is not really what I want (especially if I still want to
do something useful within the method on that data).
That's the way overload resolution works. In the case of
foo(IBase*), the compiler still considers four functions:
foo(int)
foo(double)
foo<IBase*>(IBase*)
foo(IBase*)
When passed an IBase*, the last two are exact matches, thus
equally good, and the tie-breaker comes into play to choose the
one that is not a template instantiation. In the case of:
foo(int)
foo(double)
foo<IBase*>(IBase*)
foo(IBase const*)
there's no need for a tie-breaker, since resolution based
uniquely on type prefers the third.
The obvious solution is to add an additional overload:
foo(IBase* p) { foo( const_cast< IBase const* >(p) ); }
I've tried to specialize the template method as well, i.e. I
modified the templatized foo() to look like this:
class FooCaller
{
public:
template<typename T>
void foo(const T & t);
};
Try putting the const in the right place, after what it
modifies, and you'll see where the problem comes from. When
instantiated for IBase*, that gives:
foo<IBase*>(IBase *const& t)
And the const plays no role in overload resolution.
template<typename T>
void FooCaller::foo(const T & t)
{
std::cout << "foo(T)\n";
}
template<>
void FooCaller::foo<IBase*>(const IBase* & p)
{
std::cout << "foo(const IBase*)\n";
}
This is not a specialization of your template above. It would
be a specialization of
template< typename T >
void foo( T const* )
but you haven't provided such a template. (You could, though,
but I suspect that the simplest solution is just an additional
overload of foo(T*).)
When I try to compile this, I get the following error:
main.cpp:41: error: template-id foo<IBase*> for void
FooCaller::foo(const IBase*&) does not match any template declaration
What am I doing wrong here?
Putting the const before the horses, or rather, before what it
modifies. In the presence of symbolic types which might have
compound types (typedefs or template type parameters), it's
confusing.
--
James Kanze