真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

【C++11】可變參數(shù)和lambda表達式-創(chuàng)新互聯(lián)

目錄

創(chuàng)新互聯(lián)主要從事成都做網(wǎng)站、成都網(wǎng)站設(shè)計、網(wǎng)頁設(shè)計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)隨縣,十余年網(wǎng)站建設(shè)經(jīng)驗,價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):18982081108

1.可變參數(shù)模板

1.1可變參數(shù)的模板?

1.2參數(shù)包的展開方式?

1.21遞歸函數(shù)展開參數(shù)包

1.3逗號表達式展開參數(shù)包

2.STL庫中的emplace相關(guān)接口

3.lambda表達式?

3.1lambda的引入

3.2lambda的介紹

列表使用 lambda 表達式捕獲

lambda實現(xiàn)swap函數(shù)?

lambda表達式之間不能相互賦值?


1.可變參數(shù)模板

可變參數(shù)模板可以說是C++的一大新收獲,也是C++11新增的最強大的特性之一,它對參數(shù)高度泛化,能夠讓我們創(chuàng)建可以接受可變參數(shù)的函數(shù)模板和類模板。但是可變參數(shù)模板上手需要一定的技巧性,以及對C++進行初步的認識,所以我們來具體講解一下。

1.1可變參數(shù)的模板?
template返回類型 函數(shù)名(Args... args)
{ 
   //函數(shù)主體
}
//Args+省略號叫做模板參數(shù)包,args是函數(shù)形參參數(shù)包
//Args... args代表參數(shù)從0到多個

例如:

templatevoid ShowList(Args... args)
{ 
   //函數(shù)主體
}
  • 這里的template<>不禁讓人想到了類模板和函數(shù)模板,這個其實和他很像,只不過那個指定參數(shù)了而這個Args...代表這從0到N的模板參數(shù)。
  • 最為關(guān)鍵的是Args并不是關(guān)鍵字,只不過大家都用它來形容可變參數(shù)模板,你也可以叫他Brgs還可以叫做Crgs我是沒有啥意見。

接下來我們用ShowList寫一個傳入不同參數(shù)的代碼。

//可變參數(shù)
templatevoid ShowList(Args...args)
{
	cout<< sizeof...(args)<< endl;  //獲取函數(shù)包中參數(shù)的個數(shù)
	
}

接下來傳參------》

int main()
{
	string str("hello");
	ShowList();      //0
	ShowList(1);     //1
	ShowList(11,str);  //2
	ShowList(1,'A',string("hello,world"), 12);  //4
}

特別提醒:但是我們無法直接獲取參數(shù)包中的每個參數(shù),只能通過展開參數(shù)包的方式來獲取,這是使用可變參數(shù)模板的一個主要特點?。語法并不支持使用args[i]的方式來獲取參數(shù)包中的參數(shù)同樣也不支持范圍for那個。

templatevoid ShowList(Args...args)
{
	cout<< sizeof...(args)<< endl;  //打印傳入?yún)?shù)的個數(shù)
	for (size_t i = 0; i< sizeof...(args); i++)
	{
		cout<< args[i]<< " ";
	}
	cout<< endl;
}

1.2參數(shù)包的展開方式? 1.21遞歸函數(shù)展開參數(shù)包

這里用到兩部分遞歸函數(shù),但是這兩部分遞歸函數(shù)的函數(shù)名相同:

  • 遞歸函數(shù)
  • 遞歸終止函數(shù)

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

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

每次我們都要先寫遞歸函數(shù),而遞歸終結(jié)函數(shù)可以理解為當(dāng)參數(shù)包中所有參數(shù)都被取出后,就會調(diào)用這個殺死循環(huán)的終止函數(shù)了。

templatevoid ShowList(const T& val, Args... args)
{
	cout<< val<< " ";
	ShowList(args...);
}

這個可變參數(shù)的整體就寫好了,最棘手的就是那個遞歸終結(jié)函數(shù)了。

此時這個終結(jié)函數(shù)又會分為兩類有參和無參兩種情況,這兩種情況還不太一樣。

我們先看一下無參的情況:

void ShowList()
{
	cout<< endl;
}
templatevoid ShowList(const T& val, Args... args)
{
	cout<< val<< " ";
	ShowList(args...);
}

此時當(dāng)參數(shù)包的個數(shù)為0個的時候,就會走此函數(shù),完成遞歸終止。

  • 如果外部調(diào)用ShowList函數(shù)時就沒有傳入?yún)?shù),那么就會直接匹配到無參的遞歸終止函數(shù)。
  • 而我們本意是想讓外部調(diào)用ShowList函數(shù)時匹配的都是函數(shù)模板,并不是讓外部調(diào)用時直接匹配到這個遞歸終止函數(shù)。也就是說這個通道是我的老員工用的并且我也不想借給外界人員用,所以我們不得不再給外部人員提供一個新道路。
//遞歸終止函數(shù)
void ShowListArg()
{
	cout<< endl;
}
//展開函數(shù)
templatevoid ShowListArg(T value, Args... args)
{
	cout<< value<< " "; //打印傳入的若干參數(shù)中的第一個參數(shù)
	ShowListArg(args...); //將剩下參數(shù)繼續(xù)向下傳
}

//外部人員專業(yè)通道
templatevoid ShowList(Args... args)
{
	ShowListArg(args...);
}

帶參的情況:

//遞歸終止函數(shù)-------》帶參情況
templatevoid ShowListArg(const T& val)  //終止函數(shù)帶參
{
	cout<< endl;
}
//展開函數(shù)
templatevoid ShowListArg(T value, Args... args)
{
	cout<< value<< " "; //打印傳入的若干參數(shù)中的第一個參數(shù)
	ShowListArg(args...); //將剩下參數(shù)繼續(xù)向下傳
}

//外部人員專業(yè)通道
templatevoid ShowList(Args... args)
{
	ShowListArg(args...);
}
int main()
{
	ShowListArg(1,'A',string("hello,world"), 12);  //4
}
  • 當(dāng)傳入?yún)?shù)包中參數(shù)的個數(shù)為1時,就會傳入這個終止函數(shù)中。
  • 但該方法有一個弊端就是,我們在調(diào)用ShowList函數(shù)時必須至少傳入一個參數(shù),否則就會報錯。因為此時無論是調(diào)用遞歸終止函數(shù)還是展開函數(shù),都需要至少傳入一個參數(shù)。
1.3逗號表達式展開參數(shù)包

C++只允許數(shù)組里面是同一種類型,但是模板的可變參數(shù)就意味著我參數(shù)包的類型并不統(tǒng)一,會出現(xiàn)一會是int,一會是char……。為了解決此問題,我們可以單獨封裝一層函數(shù)(PrintArg),此函數(shù)專門用于獲得參數(shù)包的每個數(shù)據(jù)并輸出,我們可以使用逗號表達式將返回值重新放到數(shù)組中。

  • 逗號表達式會從左到右依次計算各個表達式,并且將最后一個表達式的值作為返回值進行返回。
  • 將逗號表達式的最后一個表達式設(shè)置為一個整型值,確保逗號表達式返回的是一個整型值。
  • 將處理參數(shù)包中參數(shù)的動作封裝成一個函數(shù),將該函數(shù)的調(diào)用作為逗號表達式的第一個表達式。
//逗號表達式展開參數(shù)包
templatevoid PrintArg(const T& t)
{
	cout<< t<< " ";
}
//展開函數(shù)
templatevoid ShowList(Args... args)
{
	int arr[] = { (PrintArg(args), 0)... }; //列表初始化+逗號表達式
	cout<< endl;
}

int main()
{
	ShowList(1,'A',string("hello,world"), 12);  //4
}

  • 可變參數(shù)的省略號需要加在逗號表達式外面,表示需要將逗號表達式展開,如果將省略號加在args的后面,那么參數(shù)包將會被展開后全部傳入PrintArg函數(shù),代碼中的{(PrintArg(args), 0)...}將會展開成{(PrintArg(arg1), 0), (PrintArg(arg2), 0)(PrintArg(arg3), 0), etc...}。
2.STL庫中的emplace相關(guān)接口

vector - C++ Reference (cplusplus.com)?我們可以進入cplusplus官網(wǎng)搜索vector,就可以看到C++11新增的emplace

templatevoid emplace_back (Args&&... args);

&&不是特指右值引用而是代表左右值引用都可以。

我們以vector的emplace_back和push_back為例進行說明?:

  • 調(diào)用push_back函數(shù)插入元素時,可以傳入左值對象或者右值對象,也可以使用列表進行初始化。
  • 調(diào)用emplace_back函數(shù)插入元素時,也可以傳入左值對象或者右值對象,但不可以使用列表進行初始化。
  • 除此之外,emplace系列接口大的特點就是,插入元素時可以傳入用于構(gòu)造元素的參數(shù)包。
//emplace 相關(guān)應(yīng)用
int main()
{
	vector>vec;
	pairkv(22, "emplace");
	vec.push_back(kv);                                 //傳左值
	vec.push_back(pair(1222, "2022"));    //傳右值
	vec.push_back({ 30, "abc" });                      //列表初始化

	vec.emplace_back(kv);                              //傳左值
	vec.emplace_back(pair(1222, "2022")); //傳右值
	vec.emplace_back(50, "555");                       //傳參數(shù)包
	for (auto e : vec)
	{
		cout<< e.first<< ": "<< e.second<< endl;
	}
}

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

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

使用emplace的原因:

  1. 之所以說emplace系列接口更高效是因為emplace系列接口大的特點就是支持傳入?yún)?shù)包,用這些參數(shù)包直接構(gòu)造出對象,這樣就能減少一次拷貝,
  2. 但emplace系列接口并不是在所有場景下都比原有的插入接口高效,如果傳入的是左值對象或右值對象,那么emplace系列接口的效率其實和原有的插入接口的效率是一樣的。
3.lambda表達式? 3.1lambda的引入

lambda表達式有如下優(yōu)點:

  • 聲明式編程風(fēng)格:就地匿名定義目標(biāo)函數(shù)或函數(shù)對象,不需要額外寫一個命名函數(shù)或者函數(shù)對象。以更直接的方式去寫程序,好的可讀性和可維護性。
  • 簡潔:不需要額外再寫一個函數(shù)或者函數(shù)對象,避免了代碼膨脹和功能分散,讓開發(fā)者更加集中精力在手邊的問題,同時也獲取了更高的生產(chǎn)率。
  • 在需要的時間和地點實現(xiàn)功能閉包,使程序更靈活。

但是lambda具體的應(yīng)用在什么地方?--------->

在學(xué)校這個完整的教育系統(tǒng)中老師會根據(jù)學(xué)生的考試成績進行排名,而學(xué)校會根據(jù)班級成績從而對教師進行考核,現(xiàn)在學(xué)校的后勤部要根據(jù)這些指標(biāo)進行升序,降序排列。這里就會用到lambda表達式,這里一般會選擇仿函數(shù)來指定排序的主要方式。

//lambda表達式
struct Teacher
{
	string _name; // 名字
	double _point; // 班級成績
	int _evaluate; // 總評分
	Teacher(const char* str, double price, double evaluate)
		:_name(str)
		, _point(price)
		, _evaluate(evaluate)
	{}
};
struct ComparePointLess
{
	bool operator()(const Teacher& gl, const Teacher& gr)
	{
		return gl._point< gr._point;
	}
};
struct ComparePointGreater
{
	bool operator()(const Teacher& gl, const Teacher& gr)
	{
		return gl._point >gr._point;
	}
};
int main()
{
	vectorv = { { "李老師", 88, 8.9 }, { "張老師", 91, 8.7 }, { "徐老師", 86,9 }, { "王老師", 87, 8.7 } };
	sort(v.begin(), v.end(), ComparePointLess());//按成績升序排
	sort(v.begin(), v.end(), ComparePointGreater());//按成績降序排
}

這里我們就可以用lambda表達式解決這個問題了。

int main()
{
	vectorv = { { "李老師", 88, 8.9 }, { "張老師", 91, 8.7 }, { "徐老師", 86,9 }, { "王老師", 87, 8.7 } };
	sort(v.begin(), v.end(), [](const Teacher& g1, const Teacher& g2) {
		return g1._point< g2._point; });
	sort(v.begin(), v.end(), [](const Teacher& g1, const Teacher& g2) {
		return g1._point >g2._point; });
	sort(v.begin(), v.end(), [](const Teacher& g1, const Teacher& g2) {
		return g1._evaluate< g2._evaluate; });
	sort(v.begin(), v.end(), [](const Teacher& g1, const Teacher& g2) {
		return g1._evaluate >g2._evaluate; });
}
3.2lambda的介紹

lambda 表達式定義了一個匿名函數(shù),并且可以捕獲一定范圍內(nèi)的變量。lambda 表達式的語法形式可簡單歸納如下:

[ capture -list] ( params ) mutable ->return { body; };
  • 其中 capture 是捕獲列表,params 是參數(shù)表,
  • mutable 默認情況下,lambda函數(shù)總是一個const函數(shù),mutable可以取消其常量性。使用該修飾符時,參數(shù)列表不可省略(即使參數(shù)為空)。
  • return 是返回值類型,body是函數(shù)。

lambda函數(shù)的參數(shù)列表和返回值類型都是可選部分,但捕捉列表和函數(shù)體是不可省略的,因此最簡單的lambda函數(shù)如下:

int main()
{
	[]{}; //最簡單的lambda表達式
	return 0;
}

一個完整的 lambda 表達式看起來像這樣:

auto f = [](int a) ->int { return a + 1; };
std::cout<< f(1)<< std::endl; // 輸出: 2

需要注意的是,初始化列表不能用于返回值的自動推導(dǎo):

auto x1 = [](int i){ return i; };? // OK: return type is int
auto x2 = [](){ return { 1, 2 }; };? // error: 無法推導(dǎo)出返回值類型

這時我們需要顯式給出具體的返回值類型。
另外,lambda 表達式在沒有參數(shù)列表時,參數(shù)列表是可以省略的。因此像下面的寫法都是正確的:

auto f1 = [](){ return 1; };
auto f2 = []{ return 1; };? // 省略空參數(shù)表
列表使用 lambda 表達式捕獲

lambda 表達式還可以通過捕獲列表捕獲一定范圍內(nèi)的變量:

  • [] 不捕獲任何變量。
  • [&] 捕獲外部作用域中所有變量,并作為引用在函數(shù)體中使用(按引用捕獲)。
  • [bar] 按值捕獲 bar 變量,同時不捕獲其他變量。
  • [&bar] 按值捕獲外部作用域中所有變量,并按引用傳遞捕獲bar變量。
  • [=] 表示值傳遞方式捕獲所有父作用域中的變量包括this。(按值捕獲)。
  • [this] 捕獲當(dāng)前類中的 this?指針,讓 lambda 表達式擁有和當(dāng)前類成員函數(shù)同樣的訪問權(quán)限。如果已經(jīng)使用了 & 或者 =,就默認添加此選項。捕獲 this 的目的是可以在 lamda 中使用當(dāng)前類的成員函數(shù)和成員變量。

說明:

  1. 父作用域指的是包含lambda函數(shù)的語句塊。
  2. 語法上捕捉列表可由多個捕捉項組成,并以逗號分割。比如[=, &a, &b]。
  3. 捕捉列表不允許變量重復(fù)傳遞,否則會導(dǎo)致編譯錯誤。比如[=, a]重復(fù)傳遞了變量a。
  4. 在塊作用域以外的lambda函數(shù)捕捉列表必須為空,即全局lambda函數(shù)的捕捉列表必須為空。
  5. 在塊作用域中的lambda函數(shù)僅能捕捉父作用域中的局部變量,捕捉任何非此作用域或者非局部變量都會導(dǎo)致編譯報錯。
  6. lambda表達式之間不能相互賦值,即使看起來類型相同。
lambda實現(xiàn)swap函數(shù)?
//lambda實現(xiàn)swap
int main()
{
	int a=5, b=7;
	cout<<"a: "<< a<<" "<< "b: "<< b<< endl;
	auto swap = [](int& a, int& b)->void
	{
		int tmp = a;
		a = b;
		b = tmp;
	};
	swap(a, b); //別忘了分號
	cout<< "a: "<< a<< " "<< "b: "<< b<< endl;
	
	return 0;
}
  • lambda表達式是一個匿名函數(shù),該函數(shù)無法直接調(diào)用,如果想要直接調(diào)用,可借助auto將其賦值給一個變量,此時這個變量就可以像普通函數(shù)一樣使用。
  • lambda表達式的函數(shù)體在格式上并不是必須寫成一行,如果函數(shù)體太長可以進行換行,但換行后不要忘了函數(shù)體最后還有一個分號。

其實這個代碼還可以進行優(yōu)化,我們還可以利用捕捉列表進行捕捉 :

int main()
{
	int a=5, b=7;
	cout<<"a: "<< a<<" "<< "b: "<< b<< endl;
	//auto swap = [](int& a, int& b)->void
	//{
	//	int tmp = a;
	//	a = b;
	//	b = tmp;
	//};  //不要忘了后面的分號
	//swap(a, b);

	auto swap = [&a, &b]
	{
		int tmp = a;
			a = b;
			b = tmp;
	};
	swap();  //()里面不需要傳參
	cout<< "a: "<< a<< " "<< "b: "<< b<< endl;
	return 0;
}

還有的交換函數(shù)會這樣寫:

auto swap = [a, b]()mutable
	{
		int tmp = a;
		a = b;
		b = tmp;
	};
	swap();  //()里面不需要傳參
	cout<< "a: "<< a<< " "<< "b: "<< b<< endl;

注意:mutable的作用是取消lambda表達式的常量性,因為lambda總是一個const函數(shù),光取消const屬性還是不足以進行交換,這里還需要用到引用傳遞來捕獲變量進行數(shù)據(jù)交換。

lambda表達式之間不能相互賦值?

注意任何的lambda表達式之間的不能進行相互賦值,即使是兩者的類型相同。?

int main()
{
	int a = 5, b = 7;
	cout<< "a: "<< a<< " "<< "b: "<< b<< endl;
	auto swap1 = [](int& a, int& b)->void
	{
		int tmp = a;
		a = b;
		b = tmp;
	};  //不要忘了后面的分號

	auto swap2 = [](int& a, int& b)->void
	{
		int tmp = a;
		a = b;
		b = tmp;
	};  //不要忘了后面的分號
	
	cout<< typeid(swap1).name()<< endl;   // classcout<< typeid(swap2).name()<< endl;  //classreturn 0;
}
  • lambda表達式之間不能相互賦值,就算是兩個一模一樣的lambda表達式。
  • 因為lambda表達式底層的處理方式和仿函數(shù)是一樣的,在VS下,lambda表達式在底層會被處理為函數(shù)對象,該函數(shù)對象對應(yīng)的類名叫做。
  • 類名中的uuid叫做通用唯一識別碼(Universally Unique Identifier),簡單來說,uuid就是通過算法生成一串字符串,保證在當(dāng)前程序當(dāng)中每次生成的uuid都不會重復(fù)。
  • lambda表達式底層的類名包含uuid,這樣就能保證每個lambda表達式底層類名都是唯一的。
  • 因此每個lambda表達式的類型都是不同的,這也就是lambda表達式之間不能相互賦值的原因,我們可以通過typeid(變量名).name()的方式來獲取lambda表達式的類型。

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧


網(wǎng)站標(biāo)題:【C++11】可變參數(shù)和lambda表達式-創(chuàng)新互聯(lián)
文章URL:http://weahome.cn/article/coocge.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部