C++ 模板特化

       我们不可能写出对所有类型都适合的模板,某些情况下,通用模板定义对于某个类型可能是完全错误的,这个时候我们需要编写比模板函数更有效率的函数,这就是模板特化。

思考一个例子,编写比较函数的泛型函数模板:

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

当类型实参是C风格字符串的时候函数调用是有问题的。

一、函数模板的特化

1. 函数模板的声明和定义

声明:

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

如果可以从函数形参表中推断模板实参, 则不必显示之初模板实参

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

2. 函数重载和模板特化

     但是如果省去了空的模板形参表 template <> 这一行, 那就完全不一样的意义了, 变成了重载函数的声明

// generic template definition
template 
int compare(const T& t1, const T& t2)
{
    ...
}

int compare(const char* const&, const char* const &);

上面代码声明中, 最上面为模板函数, 而下面则为一个普通的重载函数的声明.

需要注意的是, 对于普通重载函数, 传入函数的实参可以应用实参转换, 而对与特化模板函数, 对实参不应用转换, 实参类型必须与特化版本的形参类型完全匹配.

3. 作用域问题

对具有同一模板实参集的同一模板, 不允许既有显示特化又有通用泛型模板实例化.

// generic template definition
template 
int compare(const T& t1, const T& t2){ ... }


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

int compare(const char* const&, const char* const &)
{ ... }

上面这个compare函数的调用就会报错, 因为在声明特化函数之前, 进行了与特化函数匹配的一次实例化.

二、类模板的特化

       考虑类似于vector的一个类, 应用到C风格的字符串上时, push_back只会复制指针, 不会复制字符串, 这样的代码会出现严重的内存问题.

1. 定义类特化模板

这里以自定义的Queue类为例, 为C风格的字符串的Queue提供正确行为, 我们给类Queue特化一个const char*版本:

template <> 
class Queue
{
public:
    // use default constructor
    // no copy control, use sythesized version for this class
    
    void push(const char* val)
    {
        real_queue.push(val);
    }

    void pop()
    {
        real_queue.pop();
    }

    bool empty() const
    {
        return real_queue.empty();
    }
    
    // return string, not const char*
    std::string front()
    {
        return real_queue.front();
    }

private:
    // forward call for generic string queue
    Queue real_queue;
}

    这是一个非常好的特化类的实现, Queue类的这个版本没有定义构造函数和复制控制构造函数, 因为它唯一的数据成员是类成员, 能够在被复制、复制、销毁时保证正确的工作. 包括push函数也只是直接调用string实例化的Queue类型对象, 将C风格字符串隐式转换为string类型来处理, front函数也是直接返回string类型, 避免对字符串数组/指针的管理. 值得一提的是,我们在特化类中尽可能的与普通泛型类模板保持一致的接口, 负责会让类的使用者感到非常迷惑.

2. 特化成员而不特化类

    当然,我们也可以选择只特化成员函数而不是去特化整个类, 来进一步简化我们的代码:

这里我们可以只特化push方法和pop方法, 实现如何复制字符串数组和释放该副本使用内存即可.

 

你可能感兴趣的:(C++)