传递派生时调用重载函数模板的正确实例 类

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>) 的调用的最佳方式是什么?


完整 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);
}

您可以使用 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;
}

Demo

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;
}

Demo

然而,这仅在您可以控制 Base 时才有效,而我认为 Jarod42 的回答在一般情况下有效。