1. 程式人生 > >C++Primer_Chap16_模板和泛型程式設計_List04_可變引數模板_筆記

C++Primer_Chap16_模板和泛型程式設計_List04_可變引數模板_筆記

  一個可變引數模板(variable template)就是一個接受可變數目引數的模板函式或模板類。可變數目的引數被稱為引數包(parameter packet)。存在兩種引數寶:

  • 模板引數包(template parameter packet),表示0個或多個模板引數
  • 函式引數包(function parameter packet),表示0個或多個函式引數。

  我們用一個省略號來指出一個模板引數或函式引數表示一個包。

  • 在一個模板引數列表中,class... 或typename...指出接下來的引數表示0個或多個型別的列表;
  • 一個型別名後面跟...表示0個或多個給定型別的非型別引數的列表。
  • 在函式引數列表中,如果一個引數的型別是一個模板引數包,則此引數也是以個函式引數包
//Args是一個模板引數包;rest是一個函式引數包
//Args表示0個或多個模板型別引數
//rest表示0個或多個函式引數
template <typename T, typename... Args>
void foo(const T &t, const Args... rest);

  當我們需要知道包中有多少元素時,可以使用sizeof...運算子,sizeof...運算子也返回一個常量表達式,且不對實參求值:

template<typename ... Args> void g(Args ... args)
{
    cout << sizeof...(Args) << endl;    //型別引數的數目
    cout << sizeof...(args) << endl;    //函式引數的數目
}

編寫可變引數函式模板

  我們可以使用一個initializer_list來定義一個可接受可變數目同種型別實參的函式。但實參型別不一致時,可變引數函式是很有用的。

  可變引數函式通常是遞迴的。第一步呼叫處理保重的第一個實參,然後用剩餘引數呼叫自身,print函式每次遞迴呼叫將第二個引數列印到第一個實參表示的流中。為了終止遞迴,還定義一個非可變引數的print函式

template<typename T>
ostream &print( ostream &os, const T &t)
{
    return os << t;
}

template<typaname T, typename... Args>
ostream &print( ostream &os, const T &t, const Args&... rest)
{
    os << t << ", ";
    returen print(os, rest...);
}

  當定義可變引數版本的print時,非可變引數版本的宣告必須出現在作用域中。否則,可變引數版本會無限遞迴。

包擴充套件

  對於一個引數包,除了獲取其大小外,我們能做的唯一的事情就是擴充套件(expand)它。當擴充套件一個包時,我們還要提供用於每個擴充套件元素的模式(pattern)。擴張一個包就是將它分解成構成的元素,對每個元素應用模式,獲得擴充套件後的列表。通過在模式右邊放一個省略號(...)來觸發擴充套件操作

template<typaname T, typename... Args>
ostream &
print( ostream &os, const T &t, const Args&... rest)    //擴充套件Args
{
    os << t << ", ";
    returen print(os, rest...);        //擴充套件rest
}


template <typename... Args>
ostream &
errorMsg(ostream &os, const Args&... rest)
{
    return print(os, debug_rep(rest)...);
}

  這個print呼叫使用了模式debug_rep(rest)。此模式表示我們希望對函式引數包rest中的每個元素呼叫debug_rep。擴充套件的結果將是一個逗號分隔的debug_rep呼叫列表。擴充套件中的模式會獨立地應用於包中的每個元素。

轉發引數包

  我們可以組合使用可變引數模板與forward機制來編寫函式,實現將其實參不變地傳遞給其他函式。

class StrVec {
public:
    template <class... Args> void emplace_back(Args&&...);
};

template<class... Args>
inline
void StrVec::emplace_back(Args&&... args)
{
    check_n_alloc();
    alloc.construct(first_free++, std::forward<Args>(args)...);
}