C++11右值引用與完美轉(zhuǎn)發(fā)及可變參數(shù)模板詳解
1.類型分類
• C++11以后,進?步對類型進?了劃分,右值被劃分純右值(pure value,簡稱prvalue)和將亡值 (expiring value,簡稱xvalue)。
• 純右值是指那些字?值常量或求值結(jié)果相當于字?值或是?個不具名的臨時對象。如: 42、 true、nullptr 或者類似 str.substr(1, 2)、str1 + str2 傳值返回函數(shù)調(diào)?,或者整形 a、b,a++,a+b 等。純右值和將亡值C++11中提出的,C++11中的純右值概念劃分等價于 C++98中的右值。
• 將亡值是指返回右值引?的函數(shù)的調(diào)?表達式和轉(zhuǎn)換為右值引?的轉(zhuǎn)換函數(shù)的調(diào)?表達,如 move(x)、static_cast(x) • 泛左值(generalized value,簡稱glvalue),泛左值包含將亡值和左值,值類別-cppreference.com和Valuecategories這兩個關(guān)于值類型的中?和英?的官??檔,有興趣可以了解細節(jié)

2.引用折疊
•C++中不能直接定義引?的引?如 int& && r = i; ,這樣寫會直接報錯,通過模板或typedef中的類型操作可以構(gòu)成引?的引?。
• 通過模板或typedef中的類型操作可以構(gòu)成引?的引?時,這時C++11給出了?個引?折疊的規(guī) 則:右值引?的右值引?折疊成右值引?,所有其他組合均折疊成左值引?
• 下?的程序中很好的展?了模板和typedef時構(gòu)成引?的引?時的引?折疊規(guī)則,?家需要?個? 個仔細理解?下。• 像f2這樣的函數(shù)模板中,T&&x參數(shù)看起來是右值引?參數(shù),但是由于引?折疊的規(guī)則,他傳遞左 值時就是左值引?,傳遞右值時就是右值引?,有些地?也把這種函數(shù)模板的參數(shù)叫做萬能引?。
• Function(T&&t)函數(shù)模板程序中,假設(shè)實參是int右值,模板參數(shù)T的推導(dǎo)int,實參是int左值,模 板參數(shù)T的推導(dǎo)int&,再結(jié)合引?折疊規(guī)則,就實現(xiàn)了實參是左值,實例化出左值引?版本形參的 Function,實參是右值,實例化出右值引?版本形參的Function。
//引用折疊限定f1實例化后總是一個左值引用
template<class T>
void f1(T& x)
{ }
//引用折疊限定f2實例化后可以是一個左值引用,也可以是一個右值引用
template<class T>
void f2(T&& x)
{ }
int main()
{
typedef int& lref;
typedef int&& rref;
int n = 0;
lref& r1 = n;//r1類型int&
lref&& r2 = n;//r2類型int&
rref& r3 = n;//r3類型int&
rref&& r4 = 1;//r4類型int&&
//即只要有左值引用折疊后就是左值引用,兩個都是右值引用才是右值引用
//沒有折疊,實例化為void f1(int&x)
f1<int>(n);
//f1<int>(0);//err
//折疊,實例化為void f1(int&x)
f1<int&>(n);
//f1<int&>(0);//err
//折疊,實例化為void f1(int&x)
f1<int&&>(n);
//f1<int&&>(0);//err
//折疊,實例化為void f1(const int&x)
f1<const int&>(n);
f1<const int&>(0);
//折疊,實例化為void f1(const int&x)
f1<const int&&>(n);
f1<const int&&>(0);
//沒有折疊,實例化為void f2(int&&x)
//f2<int>(n);//err
f2<int>(0);
//折疊,實例化為 void f2(int&x)
f2<int&>(n);
//f2<int&>(0);err
//折疊,實例化為 void f2(int&&x)
//f2<int&&>(n);err
f2<int&&>(0);
return 0;
}3.完美轉(zhuǎn)發(fā)
• Function(T&&t)函數(shù)模板程序中,傳左值實例化以后是左值引?的Function函數(shù),傳右值實例化 以后是右值引?的Function函數(shù)。
• 但是結(jié)合我們在5.2章節(jié)的講解,變量表達式都是左值屬性,也就意味著?個右值被右值引?綁定 后,右值引?變量表達式的屬性是左值,也就是說Function函數(shù)中t的屬性是左值,那么我們把t傳 遞給下?層函數(shù)Fun,那么匹配的都是左值引?版本的Fun函數(shù)。這?我們想要保持t對象的屬性, 就需要使?完美轉(zhuǎn)發(fā)實現(xiàn)。
• template T&& forward (typename remove_reference::type& arg);
•template T&& forward (typename remove_reference::type&& arg);
• 完美轉(zhuǎn)發(fā)forward本質(zhì)是?個函數(shù)模板,他主要還是通過引?折疊的?式實現(xiàn),下??例中傳遞給 Function的實參是右值,T被推導(dǎo)為int,沒有折疊,forward內(nèi)部t被強轉(zhuǎn)為右值引?返回;傳遞給 Function的實參是左值,T被推導(dǎo)為int&,引?折疊為左值引?,forward內(nèi)部t被強轉(zhuǎn)為左值引? 返回。
沒有forward即完美轉(zhuǎn)發(fā)前

加了forward即完美轉(zhuǎn)發(fā)后
4.可變參數(shù)模板
4.1 基本語法及原理
• C++11?持可變參數(shù)模板,也就是說?持可變數(shù)量參數(shù)的函數(shù)模板和類模板,可變數(shù)?的參數(shù)被稱 為參數(shù)包,存在兩種參數(shù)包:模板參數(shù)包,表?零或多個模板參數(shù);函數(shù)參數(shù)包:表?零或多個函 數(shù)參數(shù)。
• template<class...Args> void Func(Args... args) {}
• template<class...Args> void Func(Args&... args) {}
• template<class...Args> void Func(Args&&... args) {}
• 我們?省略號來指出?個模板參數(shù)或函數(shù)參數(shù)的表??個包,在模板參數(shù)列表中,class...或 typename...指出接下來的參數(shù)表?零或多個類型列表;在函數(shù)參數(shù)列表中,類型名后?跟...指出 接下來表?零或多個形參對象列表;函數(shù)參數(shù)包可以?左值引?或右值引?表?,跟前?普通模板 ?樣,每個參數(shù)實例化時遵循引?折疊規(guī)則。
• 可變參數(shù)模板的原理跟模板類似,本質(zhì)還是去實例化對應(yīng)類型和個數(shù)的多個函數(shù)。
模板,一個函數(shù)模板可以實例化出多個不同類型參數(shù)的函數(shù),比如template<class T>這個T可以是int類型,double類型等等
可變參數(shù)模板,一個可變參數(shù)模板實例化出多個不同參數(shù)個數(shù)的模板

4.2 包擴展
• 對于?個參數(shù)包,我們除了能計算他的參數(shù)個數(shù),我們能做的唯?的事情就是擴展它,當擴展?個 包時,我們還要提供?于每個擴展元素的模式,擴展?個包就是將它分解為構(gòu)成的元素,對每個元 素應(yīng)?模式,獲得擴展后的列表。我們通過在模式的右邊放?個省略號(...)來觸發(fā)擴展操作。底層 的實現(xiàn)細節(jié)如圖1所?。
• C++還?持更復(fù)雜的包擴展,直接將參數(shù)包依次展開依次作為實參給?個函數(shù)去處理。

//方法1:
void showlist()
{
// 編譯器時遞歸的終?條件,參數(shù)包是0個時,直接匹配這個函數(shù)
cout << endl;
}
template<class T,class...Args>
void showlist(T t, Args...args)
{
cout << t << " ";
// args是N個參數(shù)的參數(shù)包
// 調(diào)?ShowList,參數(shù)包的第?個傳給x,剩下N-1傳給第?個參數(shù)包
showlist(args...);
}
// 編譯時遞歸推導(dǎo)解析參數(shù)
template<class...Args>
void print(Args...args)
{
showlist(args...);
}
//方法2:
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)...);
}
//void Print(int x, string y, double z)
//{
// Arguments(GetArg(x), GetArg(y), GetArg(z));
//}4.3 emplace系列接口
• template void emplace_back (Args&&... args);
• template iterator emplace (const_iterator position, Args&&... args);
• C++11以后STL容器新增了empalce系列的接?,empalce系列的接?均為模板可變參數(shù),功能上 兼容push和insert系列,但是empalce還?持新玩法,假設(shè)容器為container,empalce還?持 直接插?構(gòu)造T對象的參數(shù),這樣有些場景會更?效?些,可以直接在容器空間上構(gòu)造T對象。
• emplace_back總體??是更?效,推薦以后使?emplace系列替代insert和push系列
• 第?個程序中我們模擬實現(xiàn)了list的emplace和emplace_back接?,這?把參數(shù)包不段往下傳遞, 最終在結(jié)點的構(gòu)造中直接去匹配容器存儲的數(shù)據(jù)類型T的構(gòu)造,所以達到了前?說的empalce?持 直接插?構(gòu)造T對象的參數(shù),這樣有些場景會更?效?些,可以直接在容器空間上構(gòu)造T對象。
• 傳遞參數(shù)包過程中,如果是 Args&&... args 的參數(shù)包,要?完美轉(zhuǎn)發(fā)參數(shù)包,?式如下 std::forward(args)... ,否則編譯時包擴展后右值引?變量表達式就變成了左值。

模擬實現(xiàn)List新增加emplace_back等接口
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace bit
{
template<class T>
struct list_node
{
T _data;
list_node* next;
list_node* prev;
/*list_node(const T& val = T())
:_data(val)
, next(nullptr)
, prev(nullptr)
{}*/
list_node() = default;
template <class... Args>
list_node(Args&&... args)
: next(nullptr)
, prev(nullptr)
, _data(forward<Args>(args)...)
{
}
};
template<class T,class ref,class ptr>
struct list_iterator
{
typedef struct list_iterator<T,ref,ptr> self;
typedef list_node<T> node;
node* _node;
list_iterator<T, ref, ptr>(node* nd)
:_node(nd)
{}
ref& operator*()
{
//return _node->data;
return _node->_data;
}
ptr* operator->()
{
return & _node->_data;
}
self& operator++()
{
_node = _node->next;
return *this;
}
self& operator--()
{
//return _node->prev;
_node = _node->prev;
return *this;
}
self& operator++(int)
{
_node = _node->next;
return *this;
}
self& operator--(int)
{
//return _node->prev;
_node = _node->prev;
return *this;
}
bool operator==(const self& s)const
{
return _node == s._node;
}
bool operator!=(const self& s)const
{
return _node != s._node;
}
};
template<class T>
class list
{
public:
typedef list_iterator<T,T&,T*> iterator;
typedef list_iterator<T,const T&, const T*>const_iterator;
typedef list_node<T> node;
list<T>()
{
empty_init();
}
void empty_init()
{
_head = new node;
_head->next = _head;
_head->prev = _head;
}
iterator begin()
{
return iterator(_head->next);
}
iterator end()
{
return iterator(_head);
}
const_iterator begin()const
{
return const_iterator(_head->next);
}
const_iterator end()const
{
return const_iterator ( _head);
}
template <class... Args>
iterator insert(iterator pos, Args&&...args)
{
node* cur = pos._node;
node* prev = cur->prev;
node* newnode = new node(forward<Args>(args)...);
newnode->next = cur;
cur->prev = newnode;
newnode->prev = prev;
prev->next = newnode;
return cur;
++size;
}
template <class... Args>
void emplace_back(Args&&... args)
{
insert(end(), forward<Args>(args)...);
}
iterator insert(iterator pos, const T& x)
{
node* cur = pos._node;
node* prev = cur->prev;
node* newnode = new node(x);
newnode->next = cur;
cur->prev = newnode;
newnode->prev = prev;
prev->next = newnode;
return cur;
++size;
}
iterator insert(iterator pos, T&& x)
{
node* cur = pos._node;
node* prev = cur->prev;
node* newnode = new node(forward<T>(x));
newnode->next = cur;
cur->prev = newnode;
newnode->prev = prev;
prev->next = newnode;
return cur;
++size;
}
void pushback(const T& x)
{
insert(end(), x);
}
void push_back(T&& x)
{
insert(end(), forward<T>(x));
}
void pushfront(const T& x)
{
insert(begin(), x);
}
iterator erase(iterator pos)
{
assert(pos!=end());
node* cur = pos._node;
node* prev = cur->prev;
node* next = cur->next;
prev->next = next;
next->prev = prev;
delete cur;
return next;
--size;
}
void popback()
{
erase(--end());
}
void popfront()
{
erase(begin());
}
size_t size1()
{
return size;
}
bool empty()
{
return size == 0;
}
list<T>(list<T>&l)
{
empty_init();
for (auto& ch : l)
{
pushback(ch);
}
}
void swap(list<T>l)
{
std::swap(_head, l._head);
std::swap(size, l.size1());
}
list<T>operator=(list<T>tmp)
{
swap(tmp);
return *this;
}
void clear()
{
auto it = begin();
while (it != end())
{
it = erase(it);
}
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
private:
node* _head;
size_t size;
};
template<class container>
void print( const container& con)
{
for (auto ch : con)
{
cout << ch << " ";
}
cout << endl;
}
}到此這篇關(guān)于C++11右值引用與完美轉(zhuǎn)發(fā)及可變參數(shù)模板詳解的文章就介紹到這了,更多相關(guān)c++11右值引用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實現(xiàn)Armadillo庫與OpenCV庫之間的數(shù)據(jù)格式轉(zhuǎn)換
在C++領(lǐng)域,數(shù)據(jù)格式轉(zhuǎn)換是常見且關(guān)鍵的任務(wù),尤其是在使用多個不同的庫進行復(fù)雜的數(shù)據(jù)處理時,Armadillo和OpenCV是兩個在科學計算和計算機視覺領(lǐng)域非常受歡迎的庫,下面我們將詳細探討如何在C++中實現(xiàn)Armadillo庫與OpenCV庫之間的數(shù)據(jù)格式轉(zhuǎn)換,需要的朋友可以參考下2025-03-03
C語言結(jié)構(gòu)體版學生成績管理系統(tǒng)
這篇文章主要為大家詳細介紹了C語言結(jié)構(gòu)體版的學生成績管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-02-02
C++ LeetCode1945題解字符串轉(zhuǎn)化后的各位數(shù)字之和
這篇文章主要為大家介紹了C++ LeetCode1945題解字符串轉(zhuǎn)化后的各位數(shù)字之和示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12

