天天看點

C++模闆與泛型(3.模闆特化)

我們編寫的模闆很多時候并不是對所有的類型都是适合的,有時候甚至是錯誤的,這種情況下,我們需要模闆特化(template specialization),模闆特化就是指某個模闆定義中的一個或多個模闆形參的實際類型或實際值是指定的。

1.函數模闆特化

函數模闆特化形式:

  • template關鍵字後接一對空尖括号<>
  • 再接模闆名和一對尖括号,尖括号中指定這個特化定義的模闆形參
  • 函數形參表
  • 函數體

在C++模闆與泛型(1.函數模闆與類模闆)中,我們提到了這樣的compare函數:

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

但是,這個函數模闆是不能用于C風格字元串的,因為如果傳入兩個C風格字元串,實際上傳入的是兩個字元串首字元的位址,比較的也是兩個位址值得大小,這并不是我們想要的結果,是以,我們需要特化的compare函數模闆:

template<> int compare<const char *>(const char* const &v1, const char* const &v2)
{
    return strcmp(v1, v2);
}
           

這裡的類型形參是const char ,是以函數形參是const char 的const引用。

我們還可以這樣寫這個函數:

int compare(const char* const &v1, const char* const &v2)
{
    return strcmp(v1, v2);
}
           

這個時候我們就定義了compare函數的重載非模闆版本。

2.類模闆特化

在C++模闆與泛型(2.編寫自己的Queue類模闆)中,我們編寫了自己的Queue類模闆,但同樣,這個Queue類也不能用于C風格字元串,因為push函數在複制給定值建立新元素時,若傳入的是C風格字元串,隻會複制指針而不會複制字元,這種共享指針的情況很容易導緻錯誤,尤其是如果指針指向動态記憶體時,可能會出現多次删除指針或誤删的情況。為解決這個問題,我們可以特化整個類,也可以特化類的部分成員:

特化類

為const char*定義整個類的特化版本:

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

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

這裡我們給Queue添加了一個資料成員:string對象的Queue,通過調用real_queue的接口實作自己的成員。

在類外部定義特化成員時,成員之前不能加template<>标記。

特化成員

Queue類應用于C風格字元串時存在的主要問題就是字元串的複制,也就是push函數需要修改,相應地,我們需要改動pop函數,于是我們可以隻特化這兩個成員:

template<> void Queue<const char*>::push(const char* const &val)
{
    char *new_item = new char[strlen(val) + 1];
    strncpy(new_item, val, strlen(val) + 1);
    QueueItem<const char*> *p = new QueueItem<const char*>(new_item);
    if(empty())
      head = tail = p;
    else{
        tail->next = p;
        tail = p;
    }
    ++size_of_queue;
}

template<> void Queue<const char*>::pop()
{
    QueueItem<const char*> *p = head;
    delete head->item;
    head = head->next;
    delete p;
    --size_of_queue;
}
           

注意,模闆特化的聲明必須在使用特化的每個檔案中出現,是以模闆特化聲明應放在頭檔案中。

繼續閱讀