Let's say I have a class template
template <class Ta>
struct Base {};
And I've written some overloaded function templates around it:
template <class T1, class T2>
void f(Base<T1>&, T2){
std::cout << "1" << std::endl;
}
template <class T1, class T2>
void f(Base<T1>&, Base<T2>&){
std::cout << "2" << std::endl;
}
When I invoke the functions:
Base<float> base1;
Base<float> base2;
f(base1,1);
f(base1,base2);
I get exactly what I'd expect:
1
2
All is well, but now I want to create a derived class:
template <class Ta, class Tb>
struct Derived : Base<Ta> {};
All of a sudden, when I run the same code on instances of the derived class:
Derived<float,int> derived1;
Derived<float,int> derived2;
f(derived1,1);
f(derived1,derived2);
I get a different result:
1
1
Apparently, the compiler interprets T2=Derived<Ta,Tb>
, and does not interpret the alternate overload as having greater specificity.
What is the best way to clarify to the compiler that calls to f(derived1,derived2)
should be interpreted as calls to f(Base<T1>, Base<T2>)
?
Full minimal example
#include <iostream>
template <class Ta>
struct Base {};
template <class Ta, class Tb>
struct Derived : Base<Ta> {};
template <class T1, class T2>
void f(Base<T1>&, T2){
std::cout << "1" << std::endl;
}
template <class T1, class T2>
void f(Base<T1>&, Base<T2>&){
std::cout << "2" << std::endl;
}
int main()
{
Base<float> base1;
Base<float> base2;
f(base1,1);
f(base1,base2);
Derived<float,int> derived1;
Derived<float,int> derived2;
f(derived1,1);
f(derived1,derived2);
}
You might use SFINAE to restrict one overload:
As your Base
is template, require custom traits (else std::is_base_of
might be used):
template <template <typename> class C, typename T>
std::true_type is_template_base_of_impl(const C<T>*);
template <template <typename> class C>
std::false_type is_template_base_of_impl(...);
template <template <typename> class C, typename T>
using is_template_base_of = decltype(is_template_base_of_impl<C>(std::declval<const T*>()));
template <class T1, class T2, std::enable_if_t<!is_template_base_of<BaseClass, T2>::value, int> = 0>
void f(Base<T1>, T2){
std::cout << "1" << std::endl;
}
template <class T1, class T2>
void f(Base<T1>, Base<T2>){
std::cout << "2" << std::endl;
}