使用模板类型参数列表重载模板函数

template function overloading with template type-parameter-list

本文关键字:函数 重载 类型参数列表      更新时间:2023-10-16

请帮助理解以下3种不同的语法

#include <iostream>
using namespace std;
template <typename T>
class Demo {
public:
   void print_type(){
        if(is_same<int,T>())
                cout << "Int type" << endl;
        if(is_same<float,T>())
                cout << "float type" << endl;
    }
 };
//1.   fun
template<typename T>   
void fun(Demo<T> a){
    cout << "In <T>fun(Demo<T>)" <<endl;
    a.print_type();
}
//2. fun
template<typename T=int,class D=Demo<T> >
void fun(D a){
    cout << "In <T,class>fun(Demo)" <<endl;
    a.print_type();
}
// 3. fun
template<typename T=int,template <typename T> class D=Demo >
void fun(D<T> a){
    cout << "In <T,template>fun(Demo<T>)" <<endl;
}
int main()
{
   fun(Demo<int>());
   fun(Demo<float>());
   return 0;
}
  1. main中的函数调用正在调用fun的第一个版本
  2. 如果我注释掉fun的任何两个定义,第三个定义称为

三种fun定义的区别是什么?我怎么调用这三个而不注释它们呢?

让我们看一下我们的三个函数:

template <typename T> void fun(Demo<T> );                           // (1)
template <typename T, typename D> void fun(D );                     // (2)
template <typename T, template <typename> class D> void fun(D<T> ); // (3)

让我们考虑第二个。D将从第一个参数推导出来,但T不会出现在任何函数参数中——它是一个非推导的上下文。因此,它必须由调用方显式指定才能被考虑。您没有指定它,因此它是演绎失败,并被抛出重载集。

现在(1)和(3)都是Demo<int>Demo<float>类型参数的可行候选。在这两种情况下,两个函数都接受相同的实参,这些实参对于调用者来说是精确匹配的,并且都是函数模板——因此最后一个决定性因素是考虑哪个函数模板更专门化(这也称为函数模板部分排序)。(1) 只能接受类似Demo<T>的类型。(3)可以接受Demo<T>,也可以接受OtherTemplate<T>YetAnotherClass<T>。它更通用——所以我们更喜欢叫它(1)。这个规则很复杂,但如果你感兴趣的话,你可以做更多的搜索。一个更简单的例子可能是:

template <typename T> void foo(T );  // (4)
template <typename T> void foo(T* ); // (5)
foo(new int(42)); 

两个候选项都是可行的,(4)与T = int*匹配,(5)与T = int匹配。但是(4)匹配任何,而(5)只匹配指针,这使得它更专门化,因此是最可行的候选者。

如果你注释掉(2),没有什么变化——它本来就不是一个可行的候选。但是如果你注释掉(1),那么我们调用(3),因为(3)成为唯一可行的候选(因此,平凡地,最好可行的候选)。