日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

变长参数模板 和 外部模板

發(fā)布時間:2024/4/18 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 变长参数模板 和 外部模板 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

變長參數(shù)模板

解釋

C++03只有固定模板參數(shù)。C++11 加入新的表示法,允許任意個數(shù)、任意類別的模板參數(shù),不必在定義時將參數(shù)的個數(shù)固定。

變長模板、變長參數(shù)是依靠C++11新引入的參數(shù)包的機(jī)制實(shí)現(xiàn)的。

?

參數(shù)包

  • 一個模板形參包(template parameter pack)是一個接受零個或多個模板實(shí)參的模板形參。

template<class?...?Types>?struct?Tuple?{?};Tuple<> t0; // Types不含任何實(shí)參Tuple<int> t1; // Types含有一個實(shí)參:intTuple<int, float> t2; // Types含有兩個實(shí)參:int和floatTuple<0> error; // 錯誤:0不是一個類型
  • 一個函數(shù)形參包(function parameter pack)是一個接受零個或多個函數(shù)實(shí)參的函數(shù)形參

template<class?...?Types>?void?f(Types...?args);f(); // OK:args不含有任何實(shí)參f(1); // OK:args含有一個實(shí)參:intf(2, 1.0); // OK:args含有兩個實(shí)參int和double
  • 一個形參包要么是一個模板形參包,要么是一個函數(shù)形參包。

  • 一個包擴(kuò)展(expansion)由一個模式(pattern)和一個省略號組成。包擴(kuò)展的實(shí)例中一個列表中產(chǎn)生零個或多個模式的實(shí)例。模式的形式依賴于擴(kuò)展所發(fā)生的上下文中

template <typename... TS> // typename... TS為模板形參包,TS為模式static void MyPrint(const char* s, TS... args) // TS... args為函數(shù)形參包,args為模式{ printf(s, args...);}

解包

一個常用的技巧是:利用模板推導(dǎo)機(jī)制,每次從參數(shù)包里面取第一個元素,縮短參數(shù)包,直到包為空

template <typename T>void fun(const T& t){ cout << t << '\n';}template <typename T, typename ... Args>void fun(const T& t, Args ... args){ cout << t << ',';??fun(args...);//遞歸解決,利用模板推導(dǎo)機(jī)制,每次取出第一個,縮短參數(shù)包的大小。}

在C++17標(biāo)準(zhǔn)中,可以使用fold expression,更直接地表達(dá),并且確保正序展開:

//?C++17template<typename T0, typename... T>void printf(T0 t0, T... t) { std::cout << t0 << std::endl; if constexpr (sizeof...(t) > 0) printf(t...);}// C++11#include <iostream>template <typename T0>void printf(T0 value){ std::cout << value << std::endl;}template <typename T, typename... Args>void printf(T value, Args... args){ std::cout << value << std::endl; printf(args...);}int main(){ printf(1, 2, "123", 1.1); return 0;}

外部模板

關(guān)鍵詞

extern

語法

extern?template?class|struct?模板名?<?實(shí)參列表?>?;????

解釋

類模板自身并不是類型、對象或任何其他實(shí)體。不會從僅含模板定義的源文件生成任何代碼。必須實(shí)例化模板以令任何代碼出現(xiàn):必須提供模板實(shí)參,使得編譯器能生成實(shí)際的類(或從函數(shù)模板生成函數(shù))。

如果外部模板聲明出現(xiàn)于某個編譯單元中,那么與之對應(yīng)的顯式實(shí)例化必須出現(xiàn)于另一個編譯單元中或者同一個編譯單元的后續(xù)代碼中;
外部模板不能用于一個靜態(tài)函數(shù)(沒有外部鏈接屬性),但可以用于類靜態(tài)成員函數(shù)

類模板實(shí)例化分為兩種:顯示實(shí)例化和隱式實(shí)例化。

顯示實(shí)例化有如下方法:

template class|struct 模板名 < 實(shí)參列表 > ;

extern template class|struct 模板名 < 實(shí)參列表 > (C++11 起);

第二種方法就是我們要說的C++11新增的方法。

隱式實(shí)例化:

當(dāng)代碼在要求完整定義的類型的語境中涉指某個模板時,或當(dāng)類型的完整性對代碼有影響,而這個特定類型尚未被顯式實(shí)例化時,發(fā)生隱式實(shí)例化。例如當(dāng)構(gòu)造此類型的對象之時,但不包括構(gòu)造指向此類型的指針之時。舉個例子:

template<class T> struct Z { void f() {} void g(); // 并不定義}; // 模板定義template struct Z<double>; // 顯式實(shí)例化 Z<double>Z<int> a; // 隱式實(shí)例化 Z<int>Z<char>* p; // 此處不實(shí)例化任何內(nèi)容p->f(); // 隱式實(shí)例化 Z<char> 而 Z<char>::f() 出現(xiàn)于此。// 并不需要且始終不實(shí)例化 Z<char>::g():不必對其進(jìn)行定義

WHY

而對于函數(shù)模板來說,現(xiàn)在我們遇到的問題和extern一個變量遇到的問題相同。不同的是,發(fā)生問題的不是變量(數(shù)據(jù)),而是函數(shù)(代碼)。這樣的困境是由于模板的實(shí)例化帶來的。

比如,我們在一個test.h的文件中聲明了如下一個模板函數(shù):

template <typename T> void fun(T) {}

在第一個test1.cpp文件中,我們定義了以下代碼:

#include "test.h"void test1() { fun(3); }

而在另一個test2.cpp文件中,我們定義了以下代碼:

#include "test.h"void test2() { fun(4); }

由于兩個源代碼使用的模板函數(shù)的參數(shù)類型一致,所以在編譯test1.cpp的時候,編譯器實(shí)例化出了函數(shù) fun(int),而當(dāng)編譯test2.cpp的時候,編譯器又再一次實(shí)例化出了函數(shù)fun(int)。那么可以想象,在test1.o目標(biāo)文件和test2.o目標(biāo)文件中,會有兩份一模一樣的函數(shù)fun(int)代碼。

?

代碼重復(fù)和數(shù)據(jù)重復(fù)不同。數(shù)據(jù)重復(fù),編譯器往往無法分辨是否是要共享的數(shù)據(jù);而代碼重復(fù),為了節(jié)省空間,保留其中之一就可以了(只要代碼完全相同)。事實(shí)上,大部分鏈接器也是這樣做的。在鏈接的時候,鏈接器通過一些編譯器輔助的手段將重復(fù)的模板函數(shù)代碼fun(int)刪除掉,只保留了單個副本。這樣一來,就解決了模板實(shí)例化時產(chǎn)生的代碼冗余問題。

?

不過讀者也注意到了,對于源代碼中出現(xiàn)的每一處模板實(shí)例化,編譯器都需要去做實(shí)例化的工作;而在鏈接時,鏈接器還需要移除重復(fù)的實(shí)例化代碼。很明顯,這樣的工作太過冗余,而在廣泛使用模板的項(xiàng)目中,由于編譯器會產(chǎn)生大量冗余代碼,會極大地增加編譯器的編譯時間和鏈接時間。解決這個問題的方法基本跟變量共享的思路是一樣的,就是使用“外部的”模板。

C++11我們可以通過下面代碼來實(shí)現(xiàn)顯示實(shí)例化:

extern template void fun<int>(int);

這樣一來,在test2.o中不會再生成fun(int)的實(shí)例代碼。由于test2.o不再包含fun(int)的實(shí)例,因此鏈接器的工作很輕松,基本跟外部變量的做法是一樣的,即只需要保證讓test1.cpp和test2.cpp共享一份代碼位置即可。而同時,編譯器也不用每次都產(chǎn)生一份fun(int)的代碼,所以可以減少編譯時間。這里也可以把外部模板聲明放在頭文件中,這樣所有包含test.h的頭文件就可以共享這個外部模板聲明了。這一點(diǎn)跟使用外部變量聲明是完全一致的。

總結(jié)

以上是生活随笔為你收集整理的变长参数模板 和 外部模板的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。