传递派生时调用重载函数模板的正确实例 类
Invoking the correct instance of an overloaded function template when passing derived classes
假设我有一个 class 模板
template <class Ta>
struct Base {};
并且我围绕它编写了一些重载函数模板:
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;
}
当我调用函数时:
Base<float> base1;
Base<float> base2;
f(base1,1);
f(base1,base2);
我得到的正是我所期望的:
1
2
一切都很好,但现在我想创建一个派生 class:
template <class Ta, class Tb>
struct Derived : Base<Ta> {};
突然之间,当我 运行 在派生的实例上使用相同的代码时 class:
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, 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);
}
您可以使用 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
:
的 non-template 父级 class
struct Base2 {};
template <class Ta>
struct Base : Base2 {};
如果这样做了,剩下的解决方案就会变得更容易一些:
template <class T1, class T2, std::enable_if_t<!std::is_base_of<Base2, 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;
}
然而,这仅在您可以控制 Base
时才有效,而我认为 Jarod42 的回答在一般情况下有效。
假设我有一个 class 模板
template <class Ta>
struct Base {};
并且我围绕它编写了一些重载函数模板:
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;
}
当我调用函数时:
Base<float> base1;
Base<float> base2;
f(base1,1);
f(base1,base2);
我得到的正是我所期望的:
1
2
一切都很好,但现在我想创建一个派生 class:
template <class Ta, class Tb>
struct Derived : Base<Ta> {};
突然之间,当我 运行 在派生的实例上使用相同的代码时 class:
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, 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);
}
您可以使用 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, class T2, std::enable_if_t<!std::is_base_of<Base2, 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;
}
然而,这仅在您可以控制 Base
时才有效,而我认为 Jarod42 的回答在一般情况下有效。