碼字不易,歡迎給個贊!
普通模板只可以採取固定數量的模板引數。然而,有時候我們希望模板可以接收任意數量的模板引數,這個時候可以採用可變引數模板。對於可變引數模板,其將包含至少乙個模板引數包,模板引數包是可以接收0個或者多個引數的模板引數。相應地,存在函式引數包,意味著這個函式引數可以接收任意數量的引數。
乙個可變引數類模板定義如下:
templateclass tuple
{};
可以用任意數量的型別來例項化tuple:
tuple<> t0;
tuplet1;
tuplet2;
// tuple<0> error; 0 is not a type
如果想避免出現用0個模板引數來例項化可變引數模板,可以這樣定義模板:
templateclass tuple
{};
此時在例項化時,必須傳入至少乙個模板引數,否則無法編譯。 同樣地,可以定義接收任意引數的可變引數函式模板:
templatevoid f(types ... args);
// 一些合法的呼叫
f();
f(1);
f(3.4, "hello");
對於類模板來說,可變模板引數包必須是模板引數列表中的最後乙個引數。但是對於函式模板來說,則沒有這個限制,考慮下面的情況:
templateclass invalid
{}; // 這是非法的定義,因為永遠無法推斷出u的型別
templatevoid valid(u u, ts ... args); // 這是合法的,因為可以推斷出u的型別
// void invalid(ts ... args, u u); // 非法的,永遠無法推斷出u
valid(1.0, 1, 2, 3); // 此時,u的型別是double,ts是
無法直接遍歷傳給可變引數模板的不同引數,但是可以借助遞迴的方式來使用可變引數模板。可變引數模板允許建立型別安全的可變長度引數列表。下面定義乙個可變引數函式模板processvalues(),它允許以型別安全的方式接受不同型別的可變數目的引數。函式processvalues()會處理可變引數列表中的每個值,對每個引數執行對應版本的handlevalue()。
// 處理每個型別的實際函式
void handlevalue(int value)
void handlevalue(double value)
void handlevalue(string value)
// 用於終止迭代的基函式
templatevoid processvalues(t arg)
// 可變引數函式模板
templatevoid processvalues(t arg, ts ... args)
可以看到這個例子用了三次... 運算子,但是有兩層不同的含義。用在引數模板列表以及函式引數列表,其表示的是引數包。前面說到,引數包可以接受任意數量的引數。用在函式實際呼叫中的...運算子,它表示引數包擴充套件,此時會對args解包,展開各個引數,並用逗號分隔。模板總是至少需要乙個引數,通過args...解包可以遞迴呼叫processvalues(),這樣每次呼叫都會至少用到乙個模板引數。對於遞迴來說,需要終止條件,當解包後的引數只有乙個時,呼叫接收乙個引數模板的processvalues()函式,從而終止整個遞迴。
假如對processvalues()進行如下呼叫:
processsvalues(1, 2.5, "test");
其產生的遞迴呼叫如下:
processsvalues(1, 2.5, "test");
handlevalue(1);
processsvalues(2.5, "test");
handlevalue(2.5);
processsvalues("test");
handlevalue("test");
由於processvalues()函式會根據實際型別推導自動呼叫正確版本的handlevalue()函式,所以這種可變引數列表是完全型別安全的。如果呼叫processvalues()函式帶有的乙個引數,無對應的handlevalue()函式版本,那麼編譯器會產生乙個錯誤。
前面的實現有乙個致命的缺陷,那就是遞迴呼叫時引數是複製傳值的,對於有些型別引數,其代價可能會很高。乙個高效且合理的方式是按引用傳值,但是對於字面量呼叫processvalues()這樣會存在問題,因為字面量僅允許傳給const引用引數。比較幸運的是,我們可以考慮右值引用。使用std::forward()函式可以實現這樣的處理,當把右值引用傳遞給processvalues()函式時,它就傳遞為右值引用,但是如果把左值引用傳遞給processvalues()函式時,它就傳遞為左值引用。下面是具體實現:
// 用於終止迭代的基函式
templatevoid processvalues(t &&arg)
// 可變引數函式模板
templatevoid processvalues(t&& arg, ts&& ... args)
這裡我們通過可變引數模板實現乙個簡化版本的printf函式:
// 基函式
void tprintf(const char* format)
templatevoid tprintf(const char* format, t&& value, ts&& ... args)
cout << *format;
}}int main()
其方法基本與processvalues()是一致的,但是由於tprintf的第乙個引數固定是const char*型別。
marc gregoire. professional c++, third edition, 2016.
cppreference parameter pack
可變引數模板
乙個可變引數模板就是乙個接受可變數目引數的模板函式或模板類。可變數目的引數被稱為引數包 parameter packet 存在兩種引數包 模板引數包 template parameter packet 表示零個或多個模板引數 函式引數包 function parameter packet 表示零個或...
可變引數模板
可變引數,也就是讓函式可以接受可變數量引數的函式。使用方法 m print 也就是0引數將會在可變引數函式m print t x,args.args 呼叫void m print 可變引數模板 可變引數模板m print t x,args.args 通過遞迴呼叫m print args.或者m pr...
可變引數模板
三個點代表此處的u為一包型別,b為一包形參,可以輸入任意多的引數,sizeof.可以檢視一包引數中的引數個數,不是占用的位元組數。引數包的展開 如何去處理引數包裡的每乙個引數呢,這裡就需要用到遞迴的展開方法 void func templatevoid func const t a,const u ...