可變參數模板
可變參數模板對參數進行了高度泛化,可以表示任意數目、任意類型的參數:
語法為:在class或者typename后面帶上省略號。
Template<class ... T>
void func(T ... args)
{//
}
T:模板參數包,args叫做函數參數包
省略號作用:
1、聲明一個包含0到任意個模板參數的參數包
2、在模板定義的右邊,可以將參數包展開一個個獨立的參數
c++11可以使用遞歸函數的方式展開參數包,獲得可變參數的每個值。不過這個需要提供一個參數包展開函數和一個遞歸終止函數。
參數Args…在展開的過程中會遞歸調用自己,每調用一次,參數包中的參數就會少一個,直到所有參數都展開為止。
右值引用
1、臨時對象的深拷貝
一個帶有堆內存的類,必須提供一個深拷貝的拷貝函數。因為默認的拷貝構造函數是淺拷貝,會發生指針懸掛問題(兩個對象的兩個指針指向同一塊內存,當函數作用結束后,兩個對象分別調用析構函數,錯誤地將一塊內存區析構兩次)。
提供深拷貝地構造函數可以保證正確,但是會造成額外地性能損耗。
GetA函數會返回臨時變量,然后通過這個臨時變量拷貝構造一個新的對象a,臨時變量在拷貝構造完成之后就銷毀了。如果這個堆內存很大地話,拷貝構造的代價很大。也就是說每次產生臨時變量都會造成額外地性能損失。
2、移動構造函數解決問題
A(A&& a) :m_ptr(a.m_ptr){a.m_ptr = nullptr;cout << "move construct" << endl;}
move construct這個構造函數是移動構造函數,它的參數是一個右值引用類型A&&。
它僅僅是將指針的所有者轉移到了另一個對象上,同時將參數對象a的指針置空。也就是說,這里僅僅做了淺拷貝,從而避免了臨時變量的深拷貝問題。
為什么能夠匹配到這個構造函數呢?
這個構造函數只能夠接受右值參數,而函數的返回值就是右值,所以就會匹配到這個構造函數。A&&看作臨時值的標識。
對于臨時值我們僅僅需要做淺拷貝即可,無需再做深拷貝,從而解決了臨時變量拷貝構造產生的性能損失問題。這就是移動語義
3、左值借助移動語義優化性能
我們可以使用std::move將左值轉換為右值引用。
move是將對象資源的所有權從一個對象轉移到另一個對象,只是轉移,沒有內存的拷貝,這就是所謂的move語義。
通常這樣使用:
使用move將左值轉換成右值引用。應用移動語義,調用移動構造函數。
我們需要記住:
如果一個對象內部有較大的內存或者動態數組,就很有必要寫move語義的拷貝構造函數和賦值函數,避免無所謂的深拷貝,以提高性能
左值引用和右值引用本質作用都是減少拷貝提高效率。
右值引用可以彌補左值引用不足的地方,右值引用做參數和做返回值減少拷貝的本質是利用了移動構造和移動賦值。
左值引用:
做參數: void func(T x) -> void func(T& x) 減少了傳參過程中的拷貝
做返回值: T func() -> T& func() 減少返回過程中的寶貝(返回對象除了作用域不存在了就不能使用傳引用了)
右值引用:
做參數: void func(T&& x) : 使func內部不再將x拷貝構造到容器空間上,而是采用移動構造
做返回值:利用移動構造減少拷貝。
4、完美轉發
右值引用會在第二次之后的參數傳遞過程中右值屬性丟失,在下一層調用中會識別成左值。
C++11引入完美轉發的概念,完美轉發是指在函數模板中,完全依照模板的參數的類型,將參數傳遞給函數模板中調用的另外一個函數
#include<iostream>
using namespace std;
void Fun(int& x)
{cout << "左值引用" << endl;
}
void Fun(int&& x)
{cout << "右值引用" << endl;
}template<typename T>
void PerfectForward(T&& t)
{Fun(std::forward<T>(t));
}int main()
{//右值引用PerfectForward(10);int a;//左值引用PerfectForward(a);//右值引用PerfectForward(std::move(a));return 0;
}
lambda表達式
lambda表達式定義了一個匿名函數,并且可以捕獲一定范圍內的變量,定義如下:
[capture] (params)mutable->return_type{statement}
[capture] :捕獲列表,捕獲上下文變量以供lambda使用。編譯器根據[]符號來判斷接下來的代碼是否是lambda函數。
(params):參數列表,與普通函數的參數列表一致,如果不需要傳遞參數,可以連同括號一起省略
mutable:修飾符,默認情況下lambda函數總是一個const函數,mutable可以取消其常量性。使用該修飾符時,參數列表不可省略
return_type:返回值類型
{statement}:函數體,內容與普通函數一樣,除了可以使用參數之外,還可以使用所捕獲的變量。
lambda表達式與普通函數最大的不同就是它可以通過捕獲列表訪問一些上下文的數據。
[var] : 表示以 值傳遞方式 捕獲 變量var
[=] : 表示以 值傳遞方式 捕獲 所有父作用域的變量(包括this)
[&var] : 表示以 引用傳遞方式 捕獲 變量var
[&] : 表示以 引用傳遞方式 捕獲 所有父作用域的變量(包括this)
[this] : 表示以 值傳遞方式 捕獲當前的this指針
lambda的類型被定義為“閉包”的類,通常用于stl庫中。在某些場景下用于簡化仿函數的使用,同時lambda作為局部函數,也會提高復雜代碼的開發速度。可以在函數內部重用代碼,不需要費心設計接口。
下面是示例代碼:
#include<iostream>
using namespace std;int main()
{int a = 0, b = 1;//實現a+b的lambda表達式auto add1 = [](int x, int y)->int {return x + y;};cout << add1(a, b) << endl;auto add2 = [a, b]()->int {return a + b;};cout << add2() << endl;cout << a << " " << b << endl;//實現a和b的交換auto swap1 = [](int& x, int& y){int tmp = x;x = y;y = tmp;};swap1(a, b);cout << "swap1() " << a << " " << b << endl;auto swap2 = [&a, &b]()mutable{int tmp = a;a = b;b = tmp;};swap2();cout << "swap2() "<< a << " " << b << endl;return 0;
}