template的用法(超详细)

假如我们有以下函数

int compare(const string &v1,const string &v2)
{
    if(v1<v2)return -1;
    if(v1>v2)return 1;
    return 0;
}

这个函数比较两个值并指出两个值的大于,小于或者等于关系。

1.函数模板

1.

template<typename T>
int compare(const T&v1,const T&v2)
{
    if(v1<v2)return -1;
    if(v1>v2)return 1;
    return 0;
}

2.使用函数模板

int main()
{
    //T是int
    cout<<compare(1,0)<<endl;
    
    //T是string
    string s1="hi",s2="world";
    cout<<compare(s1,s2)<<endl;
    return 0;

}

3.inline函数模板

inline关键字放在template之后,函数类型之前

//正确写法
template<typename T>inline T min(const T&,const T&)

//错误写法
inline template<typename T>T min(const T&,const T&)

2.类模板

1.定义

template<class Type>class Queue{
public:
    Queue();
    Type &front();
    const Type &front()const;
    void push(const Type &);
    void pop();
    bool empty()const
private:
    ...
};

2.使用类模板

与使用函数模板形成对比(1.2),使用类模板时,必须为模板形参显式指定实参

queue<int>qi;
Queue<vector<double>>qc;
Queue<sting>qs;

模板特化

有些时候,我们编写的模板定义并不总是适用于所有类型

template<typename T>
int compare(const T&v1,const T&v2)
{
    if(v1<v2)return -1;
    if(v1>v2)return 1;
    return 0;
}

当这个函数的参数类型是C风格字符串时,这个函数不能正常工作(这个函数将比较两个指针在内存中的相对位置,但没有比较指针指向对象的大小),这时候我们必须提供一个针对C风格字符串的特殊定义,这就是模板特化

1.函数模板的特化

特化的形式如下:

  • 关键字tempalte后面接一对空的尖括号(< >)
  • 函数名后接模板名和一对尖括号,尖括号中指定这个特化定义的模板形参
  • 函数形参表
  • 函数体
template<>
int compare<const char *>(const char *const &v1,const char *const &v2)
{
    return strcmp(v1,v2);
}

1.声明模板特化

与任意函数一样,函数模板特化可以声明而无须定义。

template<>
int compare<const char *>(const char *const &,const char *const &);

2.函数重载与模板特化

如果在特化中省略空的模板形参表template<>,那么结果是函数的重载而不是模板特化

int compare(const char *const&,const char *const&);
//声明了该函数的重载版本而不是模板版本

3.特化的作用域规则

在调用模板的这个特化版本之前,特化的声明必须在作用域中

template<class T>
int compare(const T&t1,const T& t2)
{
    ...
}

int main()
{
    int i=compare("hello","world");
    ...
}

template<>
int compare<const char *>(const char *const &s1,const char *const &s2)
{
    ...
}

这个程序有错误,这个函数将会调用未特化的原模板函数

特化出现在对该模板实例的调用之后是错误的

2.类模板的特化

1.类特化的定义

template<> class Queue<const char*>{
public:
    void push(const char *);
    void pop();{real_queue.pop();}
    bool empty()const{return real_queue.empty();}
    std::string front(){return real_queue.front();}
    const std::string &front()const{return real_queue.front();}
private:
    Queue<std::string>real_queue;
};

类模板特化应该与它所特化的模板定义相同的接口,否则当用户试图使用未定义的成员时会感到奇怪

2.在类特化外部定义成员

void Queue<const char *>::push(const char*val)
{
    return real_queue.push(val);
}

在类特化外部定义成员时,成员之前不能加template<>标记

3.特化成员而不特化类

template<>
void Queue<const char*>::push(const char *const &val)
{
    ...
}

template<>
void Queue<const char *>::pop()
{
    ...
}

现在,类类型Queue<const char *>将从通用类模板定义实例化而来,而push和pop函数例外。调用Queue<const char *>对象的push或pop函数时,将调用特化版本;调用其他任意成员时,将从类模板为const char*实例化一个通用版本。

4.类模板的部分特化

如果类模板有一个以上的模板形参,我们也许想要特化某些模板形参而非全部,使用类模板的部分特化可以做到这一点:

template<class T1,class T2>
class some_template{
    //...
};


//部分特化
template <class T1>
class some_template<T1,int>{
    //...
}

使用类模板的部分特化

some_template<int,string>foo;//使用类模板
some_template<string,int>bar;//使用部分特化

1.foo的实例化类型与提供的部分特化类型不匹配,因此,foo的类型必然从通用类模板实例化

2.bar选择部分特化模板来实例化。当声明了部分特化的时候,编译器将为实例化选择最特化的模板定义,当没有部分特化可以使用的时候,就使用通用模板定义。

ps:函数模板是没有部分特化的,如果声明了一个部分特化的函数模板,编译器会报错

上一篇:C++ 低级错误之 Explicit specialization of undeclared template class 'xxx'


下一篇:C++学习——模板和模板类