中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++中traits技術的示例分析

發布時間:2021-05-31 09:57:38 來源:億速云 閱讀:187 作者:小新 欄目:開發技術

這篇文章主要介紹了C++中traits技術的示例分析,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

Traits編程技法

  讓我們一點點拋出問題,然后一點點深入。

  1. 首先,在算法中運用迭代器時,很可能會用到其相應型別(迭代器所指之物的型別)。假設算法中有必要聲明一個變量,以“迭代器所指對象的型別”為型別,該怎么辦呢?

  解決方法是:利用function template的參數推導機制。

template <class I, class T>
void func_impl(I iter, T t) {
        T tmp; // 這里就是迭代器所指物的類型新建的對象
        // ... 功能實現
}

template <class I>
inline
void func(I iter) {
        func_impl(iter, *iter); // 傳入iter和iter所指的值,class自動推導
}

int main() {
    int i;
    func(&i);
}

  這里已經可以看出封裝的意思了,沒有一層impl的封裝的話,每次你都要顯式地說明迭代器指向對象型別,才能新建tmp變量。加一層封裝顯得清爽很多。

  迭代器相應型別不只是“迭代器所指對象的型別”一種而已。根據經驗,最常用的相應型別有五種,然而并非任何情況下任何一種都可以利用上述的template參數推導機制來取得。

  函數的“template參數推導機制”推導的只是參數,無法推導函數的返回值類型。萬一需要推導函數的傳回值,就無能為力了。

  2. 聲明內嵌型別似乎是個好主意,這樣我們就可以直接獲取。

template <class T>
struct MyIter {
    typedef T value_type; // 內嵌型別聲明
    // ...
};

template <class I>
typename I::value_type
func(I ite) {
    return *ite;
}

// ...
MyIter<int> ite(new int(8));
cout << func(ite);

  看起來不錯,但是并不是所有迭代器都是class type,原生指針就不行!如果不是class type,就無法為它定義內嵌型別。

  這時候就需要 偏特化 出現。

  3. 偏特化就是在特化的基礎上再加一點限制,但它還是特化的template。

 template <class I>
  struct iterator_traits {
      typedef typename I::value_type value_type;
  };
  
  template <class I>
  struct iterator_traits<T*> {
      typedef T value_type;
  };
 
 template <class I>12 typename iterator_traits<I>::value_type
 func(I ite) {
     return *ite;
 }

  func在調用 I 的時候,首先把 I 傳到萃取器中,然后萃取器就匹配最適合的 value_type。(萃取器會先匹配最特別的版本)這樣當你傳進一個原生指針的時候,首先匹配的是帶<T*>的偏特化版本,這樣 value_type 就是 T,而不是沒有事先聲明的 I::value_type。這樣返回值就可以使用 typename iterator_traits<I>::value_type 來知道返回類型。

  下面附上《STL源碼剖析》的圖片:

C++中traits技術的示例分析

讓traits干更多東西

  迭代器有常見有五種類型: value_type, difference_type, reference_type, pointer_type都比較容易在 traits 和 相應偏特化中提取。但是,iterator_category一般也有5個,這個相應型別會引發較大規模的寫代碼工程。

  例如,我們實現了 func_II, func_BI, func_RAI 分別代表迭代器類型是Input Iterator,Bidirectional Iterator和Random Access Iterator的對應實現。

  現在,當客端調用func()的時候,我們可能需要做一個判斷:

template<class Iterator>
void func(Iterator& i) {
    if (is_random_access_iterator(i))
        func_RAI(i);
    if (is_bidirectional_iterator(i))
        func_BI(i);
    else
        func_II(i);
}

  但這樣在執行時期才決定使用哪一個版本,會影響程序效率。最好能夠在編譯期就選擇正確的版本。

  重載這個函數機制可以達成這個目標。

struct input_iterator_tag {};
 struct output_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
 // ...
 // 繼承的好處就是,當函數需要用 input_iterator_tag 的時候
// 假設你傳進一個forward_iterator_tag,它會沿繼承向上找,知道符合條件

  聲明了一些列 tag 之后,我們就可以重載 func函數: func(tag)。

  到這里,各個型別的具體重載實現已經寫好,但是需要一個統一的接口,這時候 traits 就可以出場了。

template<class Iterator>
 inline void func(Iterator& i)
 {
    typedef typename Iterator_traits<Iterator>::iterator_category category;
    __func(i, category()); // 各型別的重載
 }

簡單實例代碼

  所以說,traits一方面,在面對不同的輸入類時,能找到合適的返回型別;另一方面,當型別對應有不同的實現函數的時候,能起到一個提取型別然后分流的作用。

  先假設我們有一個 func 函數,可以接受 自定義的類 或者 原始的指針 作為參數,并自動輸出使用了什么tag。

  首先根據 traits(由本身或偏特化版本實現) ,它會提取 u 的返回型別,然后調用對應的構造函數 return_type(), 來當作各個重載版本 __func 的重載標志區分不同的實際函數。

  C++中traits技術的示例分析

首先我們看看接口代碼的編寫

 template <class unknown_class>
 inline typename unknown_class_traits<unknown_class>::return_type // 萃取器取得對應型別
 func(unknown_class u) {
     typedef typename unknown_class_traits<unknown_class>::return_type return_type;
     return __func(u, return_type()); // 需要調用構造函數當tag
 }

然后是實現設定的 tag ,用來模仿前面說的 II,RAI等

 template <class unknown_class>
 inline typename unknown_class_traits<unknown_class>::return_type
 return_type(unknown_class) {
     typedef typename unknown_class_traits<unknown_class>::return_type RT;
     return RT();
 }

有了這些我們就可以測試了

struct A {};
struct B : A{};

然后是 traits 隆重登場,有兩個偏特化版本。

/*特性萃取器*/
template <class unknown_class>
struct unknown_class_traits {
    typedef typename unknown_class::return_type return_type;
};

/*特性萃取器 —— 針對原生指針*/
template <class T>
struct unknown_class_traits<T*> {
    typedef T return_type;
};

/*特性萃取其 —— 針對指向常數*/
template <class T>
struct unknown_class_traits<const T*> {
    typedef const T return_type;
};

突然忘記了交代 unknown_class 的結構,自定義的類,必須要 typedef。

 template <class AorB>
struct unknown_class {
   typedef AorB return_type;
};

最后是func各個重載版本。

template <class unknown_class>
inline typename unknown_class_traits<unknown_class>::return_type
__func(unknown_class, A) {
    cout << "use A flag" << endl;
    return A();
}

template <class unknown_class>
inline typename unknown_class_traits<unknown_class>::return_type
__func(unknown_class, B) {
    cout << "use B flag" << endl;
    return B();
}

template <class unknown_class, class T>
T
__func(unknown_class, T) {
    cout << "use origin ptr" << endl;
    return T();
}

有了這些我們就可以測試了

int main() {
    unknown_class<B> b;
    unknown_class<A> a;
    //unknown_class<int> i;
    int value = 1;
    int *p = &value;

    A v1 = func(a);
    B v2 = func(b);
    int v3 = func(p);

    char ch = getchar();
}

  可以看到,對于用自定義類傳入同一個接口,它會自動使用對應的函數,而且返回值也合適。對原始指針也適用,完美!

C++中traits技術的示例分析

下面是完整代碼:

#include <iostream>
using namespace std;

/*先定義一些tag*/
struct A {};
struct B : A{}; // 繼承的好處就是,當函數需要參數為A,
                // 而你傳入的參數為B的時候,可以往上一直找到適合的對象

/*假設有一個未知類*/
template <class AorB>
struct unknown_class {
    typedef AorB return_type;
};

/*特性萃取器*/
template <class unknown_class>
struct unknown_class_traits {
    typedef typename unknown_class::return_type return_type;
};

/*特性萃取器 —— 針對原生指針*/
template <class T>
struct unknown_class_traits<T*> {
    typedef T return_type;
};

/*特性萃取其 —— 針對指向常數*/
template <class T>
struct unknown_class_traits<const T*> {
    typedef const T return_type;
};


/*決定使用哪一個類型*/
template <class unknown_class>
inline typename unknown_class_traits<unknown_class>::return_type
return_type(unknown_class) {
    typedef typename unknown_class_traits<unknown_class>::return_type RT;
    return RT();
}

template <class unknown_class>
inline typename unknown_class_traits<unknown_class>::return_type
__func(unknown_class, A) {
    cout << "use A flag" << endl;
    return A();
}

template <class unknown_class>
inline typename unknown_class_traits<unknown_class>::return_type
__func(unknown_class, B) {
    cout << "use B flag" << endl;
    return B();
}

template <class unknown_class, class T>
T
__func(unknown_class, T) {
    cout << "use origin ptr" << endl;
    return T();
}

template <class unknown_class>
inline typename unknown_class_traits<unknown_class>::return_type
func(unknown_class u) {
    typedef typename unknown_class_traits<unknown_class>::return_type return_type;
    return __func(u, return_type());
}

int main() {
    unknown_class<B> b;
    unknown_class<A> a;
    //unknown_class<int> i;
    int value = 1;
    int *p = &value;

    A v1 = func(a);
    B v2 = func(b);
    int v3 = func(p);

    char ch = getchar();
}

感謝你能夠認真閱讀完這篇文章,希望小編分享的“C++中traits技術的示例分析”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

赣榆县| 富源县| 保康县| 华阴市| 且末县| 龙门县| 庆元县| 丁青县| 永春县| 察隅县| 侯马市| 宁德市| 雷波县| 迭部县| 和龙市| 时尚| 沈阳市| 襄樊市| 南江县| 台北市| 洪洞县| 盐山县| 林甸县| 焉耆| 万安县| 滨州市| 乐平市| 五寨县| 延吉市| 稷山县| 松桃| 盐亭县| 石台县| 高碑店市| 南昌县| 辽源市| 富蕴县| 武穴市| 连山| 本溪市| 凤凰县|