最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++11可變參數(shù)模版實(shí)例詳解

 更新時(shí)間:2026年07月13日 09:53:02   作者:小松鼠0519  
可變參數(shù)模版是 C++11 新增的特性之一,它對(duì)參數(shù)高度泛化能夠讓我們創(chuàng)建可以接受可變參數(shù)的函數(shù)模版和類模版,本文介紹C++11可變參數(shù)模版的相關(guān)知識(shí),感興趣的朋友跟隨小編一起看看吧

前言

注:這個(gè)是博主復(fù)習(xí)使用的專題,僅適用于自己以及學(xué)習(xí)過C++知識(shí)點(diǎn)的同學(xué)。

一、可變模版參數(shù)

1.1. 可變參數(shù)模版的概念

        可變參數(shù)模版是 C++11 新增的特性之一,它對(duì)參數(shù)高度泛化能夠讓我們創(chuàng)建可以接受可變參數(shù)的函數(shù)模版和類模版。

  • 在C++11之前,類模版和函數(shù)模版中只能包含固定數(shù)量的模版參數(shù),由于可變參數(shù)模版比較抽象,使用起來(lái)需要一定的技巧。
  • 在C++之前其也有可變參數(shù)的概念,比如 printf 函數(shù)就可以接收任意多個(gè)參數(shù),但這是函數(shù)參數(shù)的可變參數(shù),并不是模版的可變參數(shù)。

注:本篇博客只講解函數(shù)模板的可變參數(shù)。

1.2. 可變參數(shù)模版的定義方法

        函數(shù)的可變參數(shù)模板定義方式如下:

template<class...Args>
返回類型 函數(shù)名(Args...args)
{
    // 函數(shù)體
}

例如:

template<class... Args>
void ShowList(Args... args)
{}

注:用省略號(hào)來(lái)指出一個(gè)模版參數(shù)或函數(shù)參數(shù)的表示一個(gè)包,在模版參數(shù)列表中,class... 或typename... 指出接下來(lái)的參數(shù)表示0或多個(gè)類型列表;在函數(shù)參數(shù)列表中,類型名后面跟... 指出接下來(lái)表示0或者多個(gè)形參對(duì)象列表;函數(shù)參數(shù)包可以用左值引用或右值引用表示。每個(gè)參數(shù)實(shí)例化都遵循引用折疊規(guī)則。

現(xiàn)在調(diào)用ShowList函數(shù)時(shí)就可以傳入任意多個(gè)參數(shù)了,并且這些參數(shù)可以是不同類型的。比如:

int main()
{
	ShowList();
	ShowList(1);
	ShowList(1,'A');
	ShowList(1,'A',string("hello"));
	return 0;
}

我們可以在函數(shù)模板中通過sizeof計(jì)算參數(shù)包中參數(shù)的個(gè)數(shù)。比如:

template<class... Args>
void ShowList(Args... args)
{
	cout << sizeof...(args) << endl;
}
int main()
{
	ShowList();							// 0		
	ShowList(1);						// 1
	ShowList(1,'A');					// 2
	ShowList(1,'A',string("hello"));	// 3
	return 0;
}

        但是我們無(wú)法直接獲取參數(shù)包中的每個(gè)參數(shù),只能通過 展開參數(shù)包的方式 來(lái)獲取,這是使用可變參數(shù)模版的一個(gè)主要特點(diǎn),也是最大的難點(diǎn)。

        他的本質(zhì)有兩種:

1. 結(jié)合引用折疊規(guī)則實(shí)例化出下面的函數(shù)。

void ShowList();
void ShowList(int arg1);
void ShowList(int arg1,char arg2);
void ShowList(int arg1,char arg2,string arg3);

2. 更本質(zhì)的是生成下面的模版參數(shù)。

void ShowList();
template<class T1>
void ShowList(T1 arg1);
template<class T1,class T2>
void ShowList(T1 arg1,T2 arg2);
template<class T1, class T2,class T3>
void ShowList(T1 arg1, T2 arg2,T3 arg3);

        注:語(yǔ)法并不支持使用 args[i] 的方式來(lái)獲取參數(shù)包中的參數(shù):
 

template<class... Args>
void ShowList(Args... args)
{
	for (int i = 0; i < sizeof...(args); i++)
	{
		cout << args[i] << " ";
	}
	cout << endl;
}

 這個(gè)是錯(cuò)的。

因此要獲取參數(shù)包中的各個(gè)參數(shù),只能通過展開參數(shù)包的方式來(lái)獲取,一般我們會(huì)通過遞歸或逗號(hào)表達(dá)式來(lái)展開參數(shù)包。

1.3. 參數(shù)包的展開方式

1.3.1. 遞歸展開參數(shù)包

遞歸展開參數(shù)包的方式如下:

  • 給函數(shù)模版增加一個(gè)模版參數(shù),這樣就可以從接收到的參數(shù)包中分離出一個(gè)參數(shù)出來(lái)。
  • 在函數(shù)模版中遞歸調(diào)用該函數(shù)模版,調(diào)用時(shí)傳入剩下的參數(shù)包。
  • 如此遞歸下去,每次分離出參數(shù)包中的一個(gè)參數(shù),直到參數(shù)包中的所有參數(shù)都被取出來(lái)。

例如打印調(diào)用函數(shù)時(shí)傳入的各個(gè)參數(shù),那么函數(shù)模版可以這樣子編寫:

//展開函數(shù)
template<class T, class ...Args>
void ShowList(T value, Args... args)
{
	cout << value << " "; //打印分離出的第一個(gè)參數(shù)
	ShowList(args...);    //遞歸調(diào)用,將參數(shù)包繼續(xù)向下傳
}

這時(shí)我們面臨的問題就是,如何終止函數(shù)的遞歸調(diào)用。

編寫無(wú)參的遞歸終止函數(shù)

我們可以在剛才的基礎(chǔ)上,再編寫一個(gè)無(wú)參的遞歸終止函數(shù),該函數(shù)的函數(shù)名與展開函數(shù)的函數(shù)名相同。如下:

//遞歸終止函數(shù)
void ShowList()
{
	cout << endl;
}
//展開函數(shù)
template<class T, class ...Args>
void ShowList(T value, Args... args)
{
	cout << value << " "; //打印分離出的第一個(gè)參數(shù)
	ShowList(args...);    //遞歸調(diào)用,將參數(shù)包繼續(xù)向下傳
}
int main()
{
	ShowList();							
	ShowList(1);						
	ShowList(1, 'A');					
	ShowList(1, 'A', string("hello"));	
	return 0;
}

        這樣一來(lái),當(dāng)遞歸調(diào)用 ShowList 函數(shù)模版時(shí),如果傳入的參數(shù)包中的參數(shù)個(gè)數(shù)為0,就會(huì)直接匹配到這個(gè)無(wú)參的遞歸終止函數(shù),就是遞歸的結(jié)束函數(shù)。

        當(dāng)然也可以按照下面的寫:

void ShowList()
{
	cout << endl;
} 
template <class T, class ...Args>
void ShowList(T x, Args... args)
{
	cout << x << " ";
	ShowList(args...);
} 
// 編譯時(shí)遞歸推導(dǎo)解析參數(shù)
template <class ...Args>
void Print(Args... args)
{
	ShowList(args...);
}
int main()
{
	Print(1);
	Print(1, 'A');
	Print(1, 'A', string("hello"));
	return 0;
}

1.3.2. 逗號(hào)表達(dá)式展開參數(shù)包

1. 通過列表獲取參數(shù)包中的參數(shù)

        如果參數(shù)包中各個(gè)參數(shù)的類型都是整型,那么也可以把這個(gè)參數(shù)包放到列表當(dāng)中初始化這個(gè)整型數(shù)組,此時(shí)參數(shù)包中參數(shù)就放到數(shù)組中了。比如:

template<class...Args>
void Print(Args...args)
{
	int arr[] = { args...};
	for (auto e : arr)
	{
		cout << e << " ";
	}
	cout << endl;
}
int main()
{
	Print(1);
	Print(1,2);
	Print(1,2,3);
	return 0;
}

        但C++并不像Python這樣的語(yǔ)言,C++規(guī)定一個(gè)容器中存儲(chǔ)的數(shù)據(jù)類型必須是相同的,因此如果這樣寫的話,那么調(diào)用ShowList函數(shù)時(shí)傳入的參數(shù)只能是整型的,并且還不能傳入0個(gè)參數(shù),因?yàn)閿?shù)組的大小不能為0,因此我們還需要在此基礎(chǔ)上借助逗號(hào)表達(dá)式來(lái)展開參數(shù)包。

2. 通過逗號(hào)表達(dá)式展開參數(shù)包

        雖然我們不能用不同類型的參數(shù)去初始化一個(gè)整型數(shù)組,但我們可以借助逗號(hào)表達(dá)式。

  • 逗號(hào)表達(dá)式會(huì)從左到右依次計(jì)算各個(gè)表達(dá)式,并且將最后一個(gè)表達(dá)式的值作為返回值進(jìn)行返回。
  • 將逗號(hào)表達(dá)式的最后一個(gè)表達(dá)式設(shè)置為一個(gè)整型值,確保逗號(hào)表達(dá)式返回的是一個(gè)整型值。
  • 將處理參數(shù)包中參數(shù)的動(dòng)作封裝成一個(gè)函數(shù),將該函數(shù)的調(diào)用作為逗號(hào)表達(dá)式的第一個(gè)表達(dá)式。

        這樣一來(lái),在執(zhí)行逗號(hào)表達(dá)式時(shí)就會(huì)先調(diào)用處理函數(shù)處理對(duì)應(yīng)的參數(shù),然后再將逗號(hào)表達(dá)式中的最后一個(gè)整型值作為返回值來(lái)初始化整型數(shù)組。比如:

template<class T>
const T& GetArg(const T& x)
{
	cout << x << " ";
	return x;
}
template<class...Args>
void Arguments(Args... args)
{}
template<class... Args>
void Print(Args... args)
{
	Arguments(GetArg(args)...);
}
int main()
{
	Print(1, string("2345"), 1.1);
	return 0;
}

他的本質(zhì)其實(shí)是:
編譯器在編譯的是偶,實(shí)例化下面的函數(shù):

template<class T>
const T& GetArg(const T& x)
{
	cout << x << " ";
	return x;
}
template<class...Args>
void Arguments(Args... args)
{}
template<class... Args>
void Print(int x,string y,double z)
{
	Arguments(GetArg(x),GetArg(y),GetArg(z));
}
int main()
{
	Print(1, string("2345"), 1.1);
	return 0;
}

1.4. STL容器匯總的emplace相關(guān)接口函數(shù)

        C++11標(biāo)準(zhǔn)給STL中的容器增加emplace版本的插入接口,比如list容器的push_front、push_back和insert函數(shù),都增加了對(duì)應(yīng)的emplace_front、emplace_back和emplace函數(shù)。如下:

注意: emplace系列接口的可變模板參數(shù)類型都帶有“&&”,這個(gè)表示的是萬(wàn)能引用,而不是右值引用。

emplace系列接口的使用方式

emplace系列接口的使用方式與容器原有的插入接口的使用方式類似,但又有一些不同之處。

以list容器的emplace_back和push_back為例:

  • 調(diào)用push_back函數(shù)插入元素時(shí),可以傳入左值對(duì)象或者右值對(duì)象,也可以使用列表進(jìn)行初始化。
  • 調(diào)用emplace_back函數(shù)插入元素時(shí),也可以傳入左值對(duì)象或者右值對(duì)象,但不可以使用列表進(jìn)行初始化。
  • 除此之外,emplace系列接口最大的特點(diǎn)就是,插入元素時(shí)可以傳入用于構(gòu)造元素的參數(shù)包。

emplace系列接口的意義

        由于emplace系列接口的可變模版參數(shù)類型都是萬(wàn)能引用,因此既可以接收左值對(duì)象,也可以接收右值對(duì)象,還可以接收參數(shù)包。

  • 如果調(diào)用emplace系列接口時(shí)傳入的是左值對(duì)象,那么首先需要先在此之前調(diào)用構(gòu)造函數(shù)實(shí)例化出一個(gè)左值對(duì)象,最終在使用定位new表達(dá)式調(diào)用構(gòu)造函數(shù)對(duì)空間進(jìn)行初始化時(shí),會(huì)匹配到拷貝構(gòu)造函數(shù)。
  • 如果調(diào)用emplace系列接口時(shí)傳入的是右值對(duì)象,那么就需要在此之前調(diào)用構(gòu)造函數(shù)實(shí)例化出一個(gè)右值對(duì)象,最終在使用定位new表達(dá)式調(diào)用構(gòu)造函數(shù)對(duì)空間進(jìn)行初始化時(shí),就會(huì)匹配到移動(dòng)構(gòu)造函數(shù)。
  • 如果調(diào)用emplace系列接口時(shí)傳入的是參數(shù)包,那么就可以直接調(diào)用函數(shù)進(jìn)行插入,并且最終在使用定位new表達(dá)式調(diào)用構(gòu)造函數(shù)對(duì)空間進(jìn)行初始化,匹配到的是構(gòu)造函數(shù)

總結(jié)一下:

  • 傳入左值對(duì)象,需要調(diào)用構(gòu)造函數(shù) + 拷貝構(gòu)造函數(shù)。
  • 傳入右值對(duì)象,需要調(diào)用構(gòu)造函數(shù) + 移動(dòng)構(gòu)造函數(shù)。
  • 傳入?yún)?shù)包,只需要調(diào)用構(gòu)造函數(shù)。       

        當(dāng)然,這里的前提是容器中存儲(chǔ)的元素所對(duì)應(yīng)的類,是一個(gè)需要深拷貝的類,并且該類實(shí)現(xiàn)了移動(dòng)構(gòu)造函數(shù)。否則調(diào)用emplace系列接口時(shí),傳入左值對(duì)象和傳入右值對(duì)象的效果都是一樣的,都需要調(diào)用一次構(gòu)造函數(shù)和一次拷貝構(gòu)造函數(shù)。

        實(shí)際emplace系列接口的一部分功能和原有各個(gè)容器插入接口是重疊的,因?yàn)槿萜髟械膒ush_back、push_front和insert函數(shù)也提供了右值引用版本的接口,如果調(diào)用這些接口時(shí)如果傳入的是右值對(duì)象,那么最終也是會(huì)調(diào)用對(duì)應(yīng)的移動(dòng)構(gòu)造函數(shù)進(jìn)行資源的移動(dòng)的。

emplace接口的意義:

  • emplace系列接口最大的特點(diǎn)就是支持傳入?yún)?shù)包,用這些參數(shù)包直接構(gòu)造出對(duì)象,這樣就能減少一次拷貝,這就是為什么有人說(shuō)emplace系列接口更高效的原因。
  • 但emplace系列接口并不是在所有場(chǎng)景下都比原有的插入接口高效,如果傳入的是左值對(duì)象或右值對(duì)象,那么emplace系列接口的效率其實(shí)和原有的插入接口的效率是一樣的。
  • emplace系列接口真正高效的情況是傳入?yún)?shù)包的時(shí)候,直接通過參數(shù)包構(gòu)造出對(duì)象,避免了中途的一次拷貝。

驗(yàn)證

        如果要驗(yàn)證我們上述對(duì)emplace系列接口的說(shuō)法,需要借助一個(gè)深拷貝的類,下面模擬實(shí)現(xiàn)了一個(gè)簡(jiǎn)化版的string類,類當(dāng)中只編寫了我們需要用到的成員函數(shù)。     

namespace xxhh
{
	class string
	{
	public:
		//構(gòu)造函數(shù)
		string(const char* str = "")
		{
			cout << "string(const char* str) -- 構(gòu)造函數(shù)" << endl;
			_size = strlen(str); //初始時(shí),字符串大小設(shè)置為字符串長(zhǎng)度
			_capacity = _size; //初始時(shí),字符串容量設(shè)置為字符串長(zhǎng)度
			_str = new char[_capacity + 1]; //為存儲(chǔ)字符串開辟空間(多開一個(gè)用于存放'\0')
			strcpy(_str, str); //將C字符串拷貝到已開好的空間
		}
		//交換兩個(gè)對(duì)象的數(shù)據(jù)
		void swap(string& s)
		{
			//調(diào)用庫(kù)里的swap
			::swap(_str, s._str); //交換兩個(gè)對(duì)象的C字符串
			::swap(_size, s._size); //交換兩個(gè)對(duì)象的大小
			::swap(_capacity, s._capacity); //交換兩個(gè)對(duì)象的容量
		}
		//拷貝構(gòu)造函數(shù)(現(xiàn)代寫法)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(const string& s) -- 拷貝構(gòu)造" << endl;
			string tmp(s._str); //調(diào)用構(gòu)造函數(shù),構(gòu)造出一個(gè)C字符串為s._str的對(duì)象
			swap(tmp); //交換這兩個(gè)對(duì)象
		}
		//移動(dòng)構(gòu)造
		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移動(dòng)構(gòu)造" << endl;
			swap(s);
		}
		//拷貝賦值函數(shù)(現(xiàn)代寫法)
		string& operator=(const string& s)
		{
			cout << "string& operator=(const string& s) -- 深拷貝" << endl;
			string tmp(s); //用s拷貝構(gòu)造出對(duì)象tmp
			swap(tmp); //交換這兩個(gè)對(duì)象
			return *this; //返回左值(支持連續(xù)賦值)
		}
		//移動(dòng)賦值
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) -- 移動(dòng)賦值" << endl;
			swap(s);
			return *this;
		}
		//析構(gòu)函數(shù)
		~string()
		{
			//delete[] _str;  //釋放_(tái)str指向的空間
			_str = nullptr; //及時(shí)置空,防止非法訪問
			_size = 0;      //大小置0
			_capacity = 0;  //容量置0
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}

        由于我們?cè)趕tring的構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)和移動(dòng)構(gòu)造函數(shù)當(dāng)中均打印了一條提示語(yǔ)句,因此我們可以通過控制臺(tái)輸出來(lái)判斷這些函數(shù)是否被調(diào)用。

        下面我們用一個(gè)容器來(lái)存儲(chǔ)模擬實(shí)現(xiàn)的string,并以不同的傳參形式調(diào)用emplace系列函數(shù)。比如:

#include<list>
int main()
{
	list<pair<int, xxhh::string>> mylist;
	pair<int, xxhh::string> kv(1, "one");
	mylist.emplace_back(kv);                              //傳左值
	cout << endl;
	mylist.emplace_back(pair<int, xxhh::string>(2, "two")); //傳右值
	cout << endl;
	mylist.emplace_back(3, "three");                      //傳參數(shù)包
	return 0;
}

到此這篇關(guān)于十二、C++11可變參數(shù)模版的文章就介紹到這了,更多相關(guān)C++可變參數(shù)模版內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語(yǔ)言實(shí)現(xiàn)24點(diǎn)問題詳解

    C語(yǔ)言實(shí)現(xiàn)24點(diǎn)問題詳解

    24點(diǎn)問題就是在屏幕上輸入1?10范圍內(nèi)的4個(gè)整數(shù)(可以有重復(fù)),對(duì)它們進(jìn)行加、減、乘、除四則運(yùn)算后(可以任意的加括號(hào)限定計(jì)算的優(yōu)先級(jí)),尋找計(jì)算結(jié)果等于24的表達(dá)式。本文將通過C語(yǔ)言實(shí)現(xiàn)24點(diǎn)問題的求解,需要的可以參考一下
    2021-12-12
  • C語(yǔ)言實(shí)現(xiàn)騎士飛行棋小游戲

    C語(yǔ)言實(shí)現(xiàn)騎士飛行棋小游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)騎士飛行棋小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • wince禁止程序標(biāo)題欄上的退出按鈕示例

    wince禁止程序標(biāo)題欄上的退出按鈕示例

    這篇文章主要介紹了wince禁止程序標(biāo)題欄上的退出按鈕示例,需要的朋友可以參考下
    2014-02-02
  • C++實(shí)現(xiàn)LeetCode(159.最多有兩個(gè)不同字符的最長(zhǎng)子串)

    C++實(shí)現(xiàn)LeetCode(159.最多有兩個(gè)不同字符的最長(zhǎng)子串)

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(159.最多有兩個(gè)不同字符的最長(zhǎng)子串),本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • C++中std::condition_variable 條件變量的使用

    C++中std::condition_variable 條件變量的使用

    std::condition_variable是C++多線程編程中用于線程間同步的核心工具,適用于需要協(xié)調(diào)多個(gè)線程行為、避免資源浪費(fèi)的場(chǎng)景,下面就來(lái)介紹一下std::condition_variable 條件變量的使用,
    2025-07-07
  • C++11系列學(xué)習(xí)之類型推導(dǎo)

    C++11系列學(xué)習(xí)之類型推導(dǎo)

    這篇文章主要介紹了C++11系列學(xué)習(xí)之類型推導(dǎo),文章基于C++的相關(guān)資料展開對(duì)主題的詳細(xì)內(nèi)容介紹,具有一定的參考價(jià)值需要的小伙伴可參考一下
    2022-04-04
  • C++實(shí)現(xiàn)并優(yōu)化異常系統(tǒng)

    C++實(shí)現(xiàn)并優(yōu)化異常系統(tǒng)

    異常處理是C++的一項(xiàng)語(yǔ)言機(jī)制,用于在程序中處理異常事件,下面這篇文章主要給大家介紹了關(guān)于C++中異常的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • C語(yǔ)言鏈表實(shí)現(xiàn)貪吃蛇小游戲

    C語(yǔ)言鏈表實(shí)現(xiàn)貪吃蛇小游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言鏈表貪吃蛇小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • OpenCV 2.4.3 C++ 平滑處理分析

    OpenCV 2.4.3 C++ 平滑處理分析

    平滑也稱模糊, 是一項(xiàng)簡(jiǎn)單且使用頻率很高的圖像處理方法,本文將詳細(xì)介紹OpenCV 2.4+ C++ 平滑處理,需要了解更多的朋友可以詳細(xì)參考下
    2012-11-11
  • C語(yǔ)言實(shí)現(xiàn)數(shù)字連連看

    C語(yǔ)言實(shí)現(xiàn)數(shù)字連連看

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)數(shù)字連連看游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09

最新評(píng)論

理塘县| 望谟县| 龙州县| 九江县| 高雄市| 韶关市| 化州市| 南充市| 云安县| 大方县| 治多县| 阿拉善盟| 阳东县| 清涧县| 嵩明县| 凤冈县| 揭西县| 旌德县| 高州市| 溧水县| 门头沟区| 怀来县| 青浦区| 弥渡县| 东明县| 乌海市| 永平县| 杭州市| 桐梓县| 文成县| 昌都县| 盱眙县| 商洛市| 鄱阳县| 额济纳旗| 大连市| 华容县| 昌吉市| 三穗县| 宁晋县| 河源市|