linux 无线网卡驱动桥转发,引用和完美转发
# 右值引用 移動語意
~~~
右值引用解決了左值引用無法傳遞臨時對象和常引用傳遞的對象只讀的問題. 右值引用允許傳遞一個可變的臨時對象引用.
移動構造使用移動而非賦值語義完成構造過程, 主要用于解決函數返回值時的大量深拷貝開銷.
#include
int main(void)
{
int a = 30;
int &b = a;
//int &&c = a; /* error 右值引用不能綁定到左值 */
//int &d = a * 2; /* erro a * 2 是一個右值 */
const int e = a * 2; /* const 的引用可以綁定到一個右值 */
int && f = a * 2; /* 右值引用可以綁定到右值 */
//int && g = f; /* error 不能將一個右值引用綁定到一個變量上, 即使這個變量是右值引用類型也不行 */
int && h = std::move(f); /* ok */
return 0;
}
int r1 = 20;
int &&r2 = std::move(r1);
調用move就意味著承諾: 除了對r1賦值或銷毀它以外, 將不能再使用它.
~~~
# 左值引用和右值引用
```
std::move(a) 返回變量右值
std::ref(a) 返回變量引用 主要是考慮函數式編程(如std::bind)在使用時,是對參數直接拷貝
```
# 完美轉發
```
std::forward可以保存參數的左值或右值特性
```
# std::move()和std::forward()對比
* std::move執行到右值的無條件轉換。就其本身而言,它沒有move任何東西。
* std::forward只有在它的參數綁定到一個右值上的時候,它才轉換它的參數到一個右值。
* std::move和std::forward只不過就是執行類型轉換的兩個函數;std::move沒有move任何東西,std::forward沒有轉發任何東西。在運行期,它們沒有做任何事情。它們沒有產生需要執行的代碼,一byte都沒有。
* std::forward()不僅可以保持左值或者右值不變,同時還可以保持const、Lreference、Rreference、validate等屬性不變;
```
#include
#include
#include
#include
using namespace std;
struct A
{
A(int&& n)
{
cout << "rvalue overload, n=" << n << endl;
}
A(int& n)
{
cout << "lvalue overload, n=" << n << endl;
}
};
class B
{
public:
template
B(T1 && t1, T2 && t2, T3 && t3) :
a1_(std::forward(t1)),
a2_(std::forward(t2)),
a3_(std::forward(t3))
{
}
private:
A a1_, a2_, a3_;
};
template
std::unique_ptr make_unique1(U&& u)
{
//return std::unique_ptr(new T(std::forward(u)));
return std::unique_ptr(new T(std::move(u)));
}
template
std::unique_ptr make_unique(U&&... u)
{
//return std::unique_ptr(new T(std::forward(u)...));
return std::unique_ptr(new T(std::move(u)...));
}
int main()
{
auto p1 = make_unique1(2);
int i = 10;
auto p2 = make_unique1(i);
int j = 100;
auto p3 = make_unique(i, 2, j);
return 0;
}
```
# C++中的萬能引用和完美轉發
1. 閱讀這篇博文需要了解C++中的左值(lvalue)和右值(rvalue)的概念,詳情參見我的另外一篇博文:[C++移動語義及拷貝優化](https://theonegis.github.io/cxx/C-%E7%A7%BB%E5%8A%A8%E8%AF%AD%E4%B9%89%E5%8F%8A%E6%8B%B7%E8%B4%9D%E4%BC%98%E5%8C%96/)
2. 萬能引用和完美轉發多涉及到模板的使用,如若不是自己寫模板,則可不用關心
## 萬能引用(Universal Reference)
首先,我們來看一個例子:
~~~javascript
#include
using std::cout;
using std::endl;
template
void func(T& param) {
cout << param << endl;
}
int main() {
int num = 2019;
func(num);
return 0;
}
~~~
這樣例子的編譯輸出都沒有什么問題,但是如果我們修改成下面的調用方式呢?
~~~javascript
int main() {
func(2019);
return 0;
}
~~~
則會得到一個大大的編譯錯誤。因為上面的模板函數只能接受左值或者左值引用(左值一般是有名字的變量,可以取到地址的),我們當然可以重載一個接受右值的模板函數,如下也可以達到效果。
~~~javascript
template
void func(T& param) {
cout << "傳入的是左值" << endl;
}
template
void func(T&& param) {
cout << "傳入的是右值" << endl;
}
int main() {
int num = 2019;
func(num);
func(2019);
return 0;
}
~~~
輸出結果:
~~~javascript
傳入的是左值
傳入的是右值
~~~
第一次函數調用的是左值得版本,第二次函數調用的是右值版本。但是,有沒有辦法只寫一個模板函數即可以接收左值又可以接收右值呢?
C++ 11中有萬能引用(Universal Reference)的概念:使用`T&&`類型的形參既能綁定右值,又能綁定左值。
但是注意了:**只有發生類型推導的時候,T&&才表示萬能引用**;否則,表示右值引用。
所以,上面的案例我們可以修改為:
~~~javascript
template
void func(T&& param) {
cout << param << endl;
}
int main() {
int num = 2019;
func(num);
func(2019);
return 0;
}
~~~
## 引用折疊(Universal Collapse)
萬能引用說完了,接著來聊引用折疊(Univers Collapse),因為完美轉發(Perfect Forwarding)的概念涉及引用折疊。一個模板函數,根據定義的形參和傳入的實參的類型,我們可以有下面四中組合:
* 左值-左值 T& & # 函數定義的形參類型是左值引用,傳入的實參是左值引用
* 左值-右值 T& && # 函數定義的形參類型是左值引用,傳入的實參是右值引用
* 右值-左值 T&& & # 函數定義的形參類型是右值引用,傳入的實參是左值引用
* 右值-右值 T&& && # 函數定義的形參類型是右值引用,傳入的實參是右值引用
但是C++中不允許對引用再進行引用,對于上述情況的處理有如下的規則:
所有的折疊引用最終都代表一個引用,要么是左值引用,要么是右值引用。規則是:**如果任一引用為左值引用,則結果為左值引用。否則(即兩個都是右值引用),結果為右值引用**。
即就是前面三種情況代表的都是左值引用,而第四種代表的右值引用。
## 完美轉發(Perfect Forwarding)
下面接著說完美轉發(Perfect Forwarding),首先,看一個例子:
~~~javascript
#include
using std::cout;
using std::endl;
template
void func(T& param) {
cout << "傳入的是左值" << endl;
}
template
void func(T&& param) {
cout << "傳入的是右值" << endl;
}
template
void warp(T&& param) {
func(param);
}
int main() {
int num = 2019;
warp(num);
warp(2019);
return 0;
}
~~~
猜一下,上面的輸出結果是什么?
~~~javascript
傳入的是左值
傳入的是左值
~~~
是不是和我們預期的不一樣,下面我們來分析一下原因:
`warp()`函數本身的形參是一個萬能引用,即可以接受左值又可以接受右值;第一個`warp()`函數調用實參是左值,所以,`warp()`函數中調用`func()`中傳入的參數也應該是左值;第二個`warp()`函數調用實參是右值,根據上面所說的引用折疊規則,warp()`函數接收的參數類型是右值引用,那么為什么卻調用了調用`func()的左值版本了呢?這是因為在`warp()`函數內部,左值引用類型變為了右值,因為參數有了名稱,我們也通過變量名取得變量地址。
那么問題來了,怎么保持函數調用過程中,變量類型的不變呢?這就是我們所謂的“完美轉發”技術,在C++11中通過`std::forward()`函數來實現。我們修改我們的`warp()`函數如下:
~~~javascript
template
void warp(T&& param) {
func(std::forward(param));
}
~~~
則可以輸出預期的結果。
總結
以上是生活随笔為你收集整理的linux 无线网卡驱动桥转发,引用和完美转发的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 金山在线文档换行快捷键(金山文档里怎么换
- 下一篇: 联想linux笔记本评测,联想(leno