1. 程式人生 > >C++模板的特化詳解(函式模版特殊,類模版特化)

C++模板的特化詳解(函式模版特殊,類模版特化)

模版與特化的概念

函式模版與類模版

C++中模板分為函式模板和類模板

  • 函式模板:是一種抽象函式定義,它代表一類同構函式。

  • 類模板:是一種更高層次的抽象的類定義。

特化的概念

所謂特化,就是將泛型的東東搞得具體化一些,從字面上來解釋,就是為已有的模板引數進行一些使其特殊化的指定,使得以前不受任何約束的模板引數,或受到特定的修飾(例如const或者搖身一變成為了指標之類的東東,甚至是經過別的模板類包裝之後的模板型別)或完全被指定了下來。

模板特化的分類

針對特化的物件不同,分為兩類:函式模板的特化類模板的特化

  • 函式模板的特化

    當函式模板需要對某些型別進行特化處理,稱為函式模板的特化。

  • 類模板的特化

    當類模板內需要對某些型別進行特別處理時,使用類模板的特化。

特化整體上分為全特化偏特化
* 全特化

就是模板中模板引數全被指定為確定的型別。

全特化也就是定義了一個全新的型別,全特化的類中的函式可以與模板類不一樣。
  • 偏特化

    就是模板中的模板引數沒有被全部確定,需要編譯器在編譯時進行確定。

全特化的標誌就是產生出完全確定的東西,而不是還需要在編譯期間去搜尋適合的特化實現,貌似在我的這種理解下,全特化的 東西不論是類還是函式都有這樣的特點,

  1. 模板函式只能全特化,沒有偏特化(以後可能有)。

  2. 模板類是可以全特化和偏特化的。

template <>然後是完全和模板型別沒有一點關係的類實現或者函式定義,如果你要說,都完全確定下來了,那還搞什麼模板呀,直接定義不就完事了?

但是很多時候,我們既需要一個模板能應對各種情形,又需要它對於某個特定的型別(比如bool)有著特別的處理,這種情形下特化就是需要的了。

  • 全特化的標誌:template <>然後是完全和模板型別沒有一點關係的類實現或者函式定義
  • 偏特化的標誌:template

函式模版特化

目前的標準中,模板函式只能全特化,沒有偏特化

至於為什麼函式不能偏特化,似乎不是因為語言實現不了,而是因為偏特化的功能可以通過函式的過載完成。

函式模版的特化技巧

函式模板的特化:當函式模板需要對某些型別進行特別處理,稱為函式模板的特化。

例如,我們編寫了一個泛化的比較程式

template <class T>
int compare(const T &left, const T&right)
{
    std::cout <<"in template<class T>..." <<std::endl;
    return (left - right);
}

這個函式滿足我們的需求了麼,顯然不,它支援常見int, float等型別的資料的比較,但是不支援char*(string)型別。

所以我們必須對其進行特化,以讓它支援兩個字串的比較,因此我們實現瞭如下的特化函式。

template < >
int compare<const char*>(const char* left, const char* right)
{
    std::cout <<"in special template< >..." <<std::endl;

    return strcmp(left, right);
}

也可以

template < >
int compare(const char* left, const char* right)
{
    std::cout <<"in special template< >..." <<std::endl;

    return strcmp(left, right);
}

示例程式1–比較兩個資料

#include <iostream>
#include <cstring>

///  模版特化

template <class T>
int compare(const T left, const T right)
{
    std::cout <<"in template<class T>..." <<std::endl;
    return (left - right);
}


//  這個是一個特化的函式模版
template < >
int compare<const char*>(const char* left, const char* right)
{
    std::cout <<"in special template< >..." <<std::endl;

    return strcmp(left, right);
}
//  特化的函式模版, 兩個特化的模版本質相同, 因此編譯器會報錯
// error: redefinition of 'int compare(T, T) [with T = const char*]'|
//template < >
//int compare(const char* left, const char* right)
//{
//    std::cout <<"in special template< >..." <<std::endl;
//
//    return strcmp(left, right);
//}


//  這個其實本質是函式過載
int compare(char* left, char* right)
{
    std::cout <<"in overload function..." <<std::endl;

    return strcmp(left, right);
}

int main( )
{
    compare(1, 4);

    const char *left = "gatieme";
    const char *right = "jeancheng";
    compare(left, right);

    return 0;
}

函式模版的特化,當函式呼叫發現有特化後的匹配函式時,會優先呼叫特化的函式,而不再通過函式模版來進行例項化。

示例程式二-判斷兩個資料是否相等

#include <iostream>
#include <cstring>

using namespace std;
//函式模板
template<class T>
bool IsEqual(T t1,T t2){
    return t1==t2;
}

template<> //函式模板特化
bool IsEqual(char *t1,char *t2){
    return strcmp(t1,t2)==0;
}

int main(int argc, char* argv[])
{
    char str1[]="abc";
    char str2[]="abc";
    cout<<"函式模板和函式模板特化"<<endl;
    cout<<IsEqual(1,1)<<endl;
    cout<<IsEqual(str1,str2)<<endl;

    return 0;
}

類模版特化

類模板的特化:與函式模板類似,當類模板內需要對某些型別進行特別處理時,使用類模板的特化。例如:

這裡歸納了針對一個模板引數的類模板特化的幾種型別

  • 一是特化為絕對型別;

  • 二是特化為引用,指標型別;

  • 三是特化為另外一個類模板。

這裡用一個簡單的例子來說明這三種情況:

特化為絕對型別

也就是說直接為某個特定型別做特化,這是我們最常見的一種特化方式, 如特化為float, double等

#include <iostream>
#include <cstring>
#include <cmath>
// general version
template<class T>
class Compare
{
public:
    static bool IsEqual(const T& lh, const T& rh)
    {
        std::cout <<"in the general class..." <<std::endl;
        return lh == rh;
    }
};



// specialize for float
template<>
class Compare<float>
{
public:
    static bool IsEqual(const float& lh, const float& rh)
    {
        std::cout <<"in the float special class..." <<std::endl;

        return std::abs(lh - rh) < 10e-3;
    }
};

// specialize for double
template<>
class Compare<double>
{
public:
    static bool IsEqual(const double& lh, const double& rh)
    {
        std::cout <<"in the double special class..." <<std::endl;

        return std::abs(lh - rh) < 10e-6;
    }
};


int main(void)
{
    Compare<int> comp1;
    std::cout <<comp1.IsEqual(3, 4) <<std::endl;
    std::cout <<comp1.IsEqual(3, 3) <<std::endl;

    Compare<float> comp2;
    std::cout <<comp2.IsEqual(3.14, 4.14) <<std::endl;
    std::cout <<comp2.IsEqual(3, 3) <<std::endl;

    Compare<double> comp3;
    std::cout <<comp3.IsEqual(3.14159, 4.14159) <<std::endl;
    std::cout <<comp3.IsEqual(3.14159, 3.14159) <<std::endl;
    return 0;
}

如果期望使用偏特化,那麼

template<class T1, class T2>
class A
{
}

template<class T1>
class A<T1, int>
{
}

特化為引用,指標型別

template <class _Iterator>
struct iterator_traits {
  typedef typename _Iterator::iterator_category iterator_category;
  typedef typename _Iterator::value_type        value_type;
  typedef typename _Iterator::difference_type   difference_type;
  typedef typename _Iterator::pointer           pointer;
  typedef typename _Iterator::reference         reference;
};

// specialize for _Tp*
template <class _Tp>
struct iterator_traits<_Tp*> {
  typedef random_access_iterator_tag iterator_category;
  typedef _Tp                         value_type;
  typedef ptrdiff_t                   difference_type;
  typedef _Tp*                        pointer;
  typedef _Tp&                        reference;
};

// specialize for const _Tp*
template <class _Tp>
struct iterator_traits<const _Tp*> {
  typedef random_access_iterator_tag iterator_category;
  typedef _Tp                         value_type;
  typedef ptrdiff_t                   difference_type;
  typedef const _Tp*                  pointer;
  typedef const _Tp&                  reference;
};

當然,除了T*, 我們也可以將T特化為 const T*, T&, const T&等,以下還是以T*為例:

// specialize for T*
template<class T>
class Compare<T*>
{
public:
    static bool IsEqual(const T* lh, const T* rh)
    {
        return Compare<T>::IsEqual(*lh, *rh);
    }
};

這種特化其實是就不是一種絕對的特化, 它只是對型別做了某些限定,但仍然保留了其一定的模板性,這種特化給我們提供了極大的方便, 如這裡, 我們就不需要對int*, float*, double*等等型別分別做特化了。

這其實是第二種方式的擴充套件,其實也是對型別做了某種限定,而不是絕對化為某個具體型別,如下:

// specialize for vector<T>
template<class T>
class Compare<vector<T> >
{
public:
    static bool IsEqual(const vector<T>& lh, const vector<T>& rh)
    {
        if(lh.size() != rh.size()) return false;
        else
        {
            for(int i = 0; i < lh.size(); ++i)
            {
                if(lh[i] != rh[i]) return false;
            }
        }
        return true;
    }
};

這就把IsEqual的引數限定為一種vector型別, 但具體是vector還是vector, 我們可以不關心, 因為對於這兩種型別,我們的處理方式是一樣的,我們可以把這種方式稱為“半特化”。

當然, 我們可以將其“半特化”為任何我們自定義的模板類型別:

// specialize for any template class type
template <class T1> 
struct SpecializedType
{
    T1 x1;
    T1 x2;
};
template <class T>
class Compare<SpecializedType<T> >
{
public:
    static bool IsEqual(const SpecializedType<T>& lh, const SpecializedType<T>& rh)
    {
        return Compare<T>::IsEqual(lh.x1 + lh.x2, rh.x1 + rh.x2);
    }
};

這就是三種類型的模板特化, 我們可以這麼使用這個Compare類:

   // int
    int i1 = 10;
    int i2 = 10;
    bool r1 = Compare<int>::IsEqual(i1, i2);

    // float
    float f1 = 10;
    float f2 = 10;
    bool r2 = Compare<float>::IsEqual(f1, f2);

    // double
    double d1 = 10;
    double d2 = 10;
    bool r3 = Compare<double>::IsEqual(d1, d2);

    // pointer
    int* p1 = &i1;
    int* p2 = &i2;
    bool r4 = Compare<int*>::IsEqual(p1, p2);

    // vector<T>
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(2);

    vector<int> v2;
    v2.push_back(1);
    v2.push_back(2);
    bool r5 = Compare<vector<int> >::IsEqual(v1, v2);

    // custom template class 
    SpecializedType<float> s1 = {10.1f,10.2f};
    SpecializedType<float> s2 = {10.3f,10.0f};
    bool r6 = Compare<SpecializedType<float> >::IsEqual(s1, s2);

相關推薦

C++模板函式模版特殊模版

模版與特化的概念 函式模版與類模版 C++中模板分為函式模板和類模板 函式模板:是一種抽象函式定義,它代表一類同構函式。 類模板:是一種更高層次的抽象的類定義。 特化的概念 所謂特化,就是將泛型的東東搞得具體化一些,從字面上

BeautifulSoup 使用select方法通過標簽名 id組合屬性查找

chrome red 例如 ttl app user encode and 定位 import requestsfrom bs4 import BeautifulSoupblslib="html5lib"user_agent="Mozilla/5.0 (Macintosh;

ThreadLocal不看後悔一看必懂

ThreadLocal概要 我們可以使用synchorinized 關鍵字來為變數加鎖以解決執行緒安全問題,從而限制只能有一個執行緒來使用此變數,但是加鎖會大大影響程式執行效率 ThreadLocal是執行緒區域性變數(從執行緒的角度看,目標變數就像是執行緒的本地變數), 通常

ThreadPoolExecutor不看後悔一看必懂

構造器引數分析 第四個構造器原始碼 corePoolSize:核心池的大小 預設情況下,在建立了執行緒池後,執行緒池中的執行緒數為0,當有任務來之後,就會建立一個執行緒去執行任務,當執行緒池中的執行緒數數目少於corePoolSize時,有新任務進來就新建一個執行緒,即使

VS2015+QT5.8 程式打包釋出包含圖片打包附工具和原始碼

之前按照百度的釋出教程,在自己的電腦打得開,在別人電腦打不開,弄了一會,總結的經驗如下: 1.打包程式 第一步:在vs2015中,以release方式除錯程式,然後會在專案資料夾的x64\relea

Android XML shape 標籤使用apk瘦身減少記憶體好幫手

Android XML shape 標籤使用詳解   一個android開發者肯定懂得使用 xml 定義一個 Drawable,比如定義一個 rect 或者 circle 作為一個 View 的背景。但是,也肯定也有人在能使用 Drawable 的地方選擇使用一

Laravel Excel實現Excel/CSV檔案匯入匯出的功能合併單元格設定單元格樣式

Laravel Excel實現Excel/CSV檔案匯入匯出(合併單元格,設定單元格樣式) 這篇文章主要給大家介紹了關於在Laravel中如何使用Laravel Excel實現Excel/CSV檔案匯入匯出功能的相關資料,文中通過示例程式碼介紹的非常詳細,對大

樹狀陣列處理線上資料求逆序對

一、樹狀陣列概論。 一、1 樹狀陣列C[I]含義:C[i]陣列的含義為從I開始,向左數一個特殊值(low=I and -i)這個區間內元素的和。也就是說C[i]:=Sum(A[j],i-low+1=<k<=i) 一、2 A陣列重要嗎:不重要 二、樹

C++繼承之二——派生成員函式函式隱藏、建構函式與相容覆蓋規則

  在這一篇文章開始之前,我先解決一個問題。   在上一篇C++繼承詳解之一——初探繼承中,我提到了在派生類中可以定義一個與基類成員函式同名的函式,這樣派生類中的函式就會覆蓋掉基類的成員函式。   在譚浩強的C++程式設計這本書第十一章,351頁最下面有這麼

C/C++中作用域

防止 局部作用域 gist 文件中 方式 為什麽不使用 形式參數 lan archive 作用域規則告訴我們一個變量的有效範圍,它在哪兒創建,在哪兒銷毀(也就是說超出了作用域)。變量的有效作用域從它的定義點開始,到和定義變量之前最鄰近的開括號配對的第一個閉括號。也就是說,作

C++: I/O流——串流

name namespace 轉換 pac end 成員 col logs nbsp 一、串流 串流類是 ios 中的派生類 C++的串流對象可以連接string對象或字符串 串流提取數據時對字符串按變量類型解釋;插入數據時把類型 數據轉換成字符串 串流I/O具有格式化功能

C#委托使用Delegates

新的 返回值 一個 個人 執行 第一個 urn 語法 垃圾回收 摘要 委托是C#編程一個非常重要的概念,也是一個難點。本文將系統詳細講解委托。 1. 委托是什麽? 其實,我一直思考如何講解委托,才能把委托說得更透徹。說實話,每個人都委托都有不同的見解,因為看問題的角度不

Python C AP的使用

nds 項目 namespace 以及 int float 數據大小 mem else 簡介 介紹一下Python虛擬機的初始化及退出,Python基本數據類型的對象創建以及C和Python之間的數據類型互相轉換。 Python虛擬機的初始化及退出 初始化Python虛擬機

vue-cli生成的模板各個檔案 vue-cli腳手架中webpack配置基礎檔案

vue-cli腳手架中webpack配置基礎檔案詳解 一、前言 原文:https://segmentfault.com/a/1190000014804826 vue-cli是構建vue單頁應用的腳手架,輸入一串指定的命令列從而自動生成vue.js+wepack的專案模板。這其中webpack發揮了很大

機器學習中L1L2規則先驗及稀疏性解釋

(作者:陳玓玏) 1、 為什麼要正則化? 知乎上有個兄弟說得對(https://www.zhihu.com/question/20924039 這個問題下Stark Einstein的回答),不應該說是正則化,應該說是規則化,也就是說,我們原來是在完全沒有任何先

C++中繼承和派生公有、私有、保護繼承的區別

繼承和派生概述: 繼承和派生是同一個過程從不同角的度看 保持已有類的特性而構造新類的過程稱為繼承。 在已有類的基礎上新增自己的特性而產生新類的過程稱為派生。 被繼承的已有類稱為基類(或父類)。 派生出的新類稱為派生類(

C++文件操作ifstream、ofstream、fstream

  轉自:http://www.cnblogs.com/azraelly/archive/2012/04/14/2446914.html C++ 通過以下幾個類支援文件的輸入輸出: ofstream: 寫操作(輸出)的文件類 (由ostream引申而來) ifstr

單例模式餓漢式懶漢式登記式列舉式序列和反序列

一、單例模式應用場景:保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。 應用場景:保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。 Spring 中的單例模式完成了後半句話,即提供了全域性的訪問點 BeanFactory。但沒有從構造器級別去 控制單例,這是

C++檔案讀寫ofstream,ifstream,fstream

在看C++程式設計思想中,每個練習基本都是使用ofstream,ifstream,fstream,以前粗略知道其用法和含義,在看了幾位大牛的博文後,進行整理和總結: 這裡主要是討論fstream的內容: #include <fstream> ofstrea

c/c++預處理過程之條件編譯及預定義的巨集

未經博主同意不得私自轉載!不準各種形式的貼上複製本文及盜圖! 首先對於上篇文章中巨集定義的補充: (1)#define NAME"zhangyuncong" 程式中有"NAME"則,它會不會被替換呢? (2)#define 0x abcd 可以嗎?也就是說,可不可以用不是