问题描述
假设我有一个课程模板
template <class Ta>
struct Base {};
并且我已经写了一些重载的函数模板:
template <class T1,class T2>
void f(Base<T1>&,T2){
std::cout << "1" << std::endl;
}
template <class T1,Base<T2>&){
std::cout << "2" << std::endl;
}
当我调用函数时:
Base<float> base1;
Base<float> base2;
f(base1,1);
f(base1,base2);
我完全符合我的期望:
1
2
一切都很好,但是现在我想创建一个派生类:
template <class Ta,class Tb>
struct Derived : Base<Ta> {};
突然之间,当我在派生类的实例上运行相同的代码时:
Derived<float,int> derived1;
Derived<float,int> derived2;
f(derived1,1);
f(derived1,derived2);
我得到不同的结果:
1
1
显然,编译器会解释T2=Derived<Ta,Tb>
,而不会将替代重载解释为具有更高的特异性。
向编译器澄清的最佳方法是,将对f(derived1,derived2)
的调用解释为对f(Base<T1>,Base<T2>)
的调用?
#include <iostream>
template <class Ta>
struct Base {};
template <class Ta,class Tb>
struct Derived : Base<Ta> {};
template <class T1,Base<T2>&){
std::cout << "2" << std::endl;
}
int main()
{
Base<float> base1;
Base<float> base2;
f(base1,base2);
Derived<float,derived2);
}
解决方法
您可以使用SFINAE限制一个重载:
由于您的Base
是模板,因此需要自定义特征(可能使用其他std::is_base_of
):
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;
}
,
Jarod42的答案使我研究了条件编译解决方案,并使用std::is_base_of
找到了有效的解决方案。但是,由于在这个示例中Base
是模板,因此该解决方案需要创建Base
的非模板父类:
struct Base2 {};
template <class Ta>
struct Base : Base2 {};
如果这样做,其余的解决方案将变得更加简单:
template <class T1,std::enable_if_t<!std::is_base_of<Base2,Base<T2>){
std::cout << "2" << std::endl;
}
但是,这仅在您拥有Base
的控制权时才有效,而我认为Jarod42的答案在一般情况下适用。