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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

编程问答

变长参数模板 和 外部模板

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

變長(zhǎng)參數(shù)模板

解釋

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

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

?

參數(shù)包

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

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

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

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

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

解包

一個(gè)常用的技巧是:利用模板推導(dǎo)機(jī)制,每次從參數(shù)包里面取第一個(gè)元素,縮短參數(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ī)制,每次取出第一個(gè),縮短參數(shù)包的大小。}

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

//?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

語(yǔ)法

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

解釋

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

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

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

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

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

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

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

隱式實(shí)例化:

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

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():不必對(duì)其進(jìn)行定義

WHY

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

比如,我們?cè)谝粋€(gè)test.h的文件中聲明了如下一個(gè)模板函數(shù):

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

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

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

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

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

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

?

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

?

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

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

extern template void fun<int>(int);

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

總結(jié)

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

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