C++修煉:C++11(二)

?????????Hello大家好!很高興我們又見面啦!給生活添點passion,開始今天的編程之路!

我的博客:<但凡.

我的專欄:《編程之路》、《數據結構與算法之美》、《題海拾貝》、《C++修煉之路》

歡迎點贊,關注!

目錄

1、可變參數模板

? ? ? ? 1.1、基本語法

? ? ? ? 1.2、包擴展

? ? ? ? 1.3、emplace系列接口

2、類的新功能

? ? ? ? 2.1、默認的移動構造和移動賦值

? ? ? ? 2.2、委托構造

? ? ? ? 2.3、default和delete


1、可變參數模板

????????可變參數模板(Variadic Templates)是C++11引入的一個重要特性,它允許模板接受任意數量和類型的參數。

? ? ? ? 1.1、基本語法

? ? ? ? 可變參數模板可以使函數模板或者類模板支持任意多個類型的變量。可變數目的參數被稱為參數包。存在兩種參數包,第一種是模板參數包,第二種是函數參數包。參數包可以接受0個參數。

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

? ? ? ? 比如,在上面三個函數模板中,Arges是模板參數包,arges是函數參數包。我們需要注意一下格式,注意一下三個點的位置。

? ? ? ? 對于上面的三個函數模板,第三個函數模板我們使用的是右值引用,這意味著對于參數包中的每個類型都是使用的萬能引用。如果傳左值,這個參數包中的類型被推導為左值引用,引用折疊后為左值引用。如果傳右值的話,類型被推導為右值引用,折疊后為右值引用。

????????可變參數模板的原理跟模板類似,本質還是去實例化對應類型和個數的多個函數。

? ? ? ? 我們可以使用sizeof...來計算參數包中參數的個數。

#include<iostream>
using namespace std;template<class ...Args> 
void func(Args... args) 
{cout << sizeof...(args) << endl;
}
int main()
{func();//0func(1);//1func(1, 2);//2func(1, 2,"sss");//3return 0;
}

? ? ? ? 這個計算也是編譯時計算,因為本質上我們就是實例化出四個函數。其實參數包也是使模板進一步的泛型化。

? ? ? ? 本質上是替換了這四個函數:

void func()
{}
void func(int a)
{}
void func(int a,int b)
{}
void func(int a,int b,const char* str)
{}

? ? ? ? 1.2、包擴展

? ? ? ? 現在我們實現一個print,對于傳進函數的每一個變量都打印一次。

? ? ? ? ?注意參數包不能這樣用,因為參數包不是一個容器:

template <class ...Args>
//void Print(Args... args)
//{
// // 可變參數模板編譯時解析 
// // 下?是運?獲取和解析,所以不?持這樣? 
// cout << sizeof...(args) << endl;
// for (size_t i = 0; i < sizeof...(args); i++)
// {
// cout << args[i] << " ";
// }
// cout << endl;
//}

? ? ? ? 那么我們怎么實現print函數呢?我們可以使用包擴展來實現。

? ? ? ? 包擴展是在編譯時進行的。包擴展本質上是遞歸調用,但是是在編譯時遞歸。

void ShowList()
{// 編譯器時遞歸的終?條件,參數包是0個時,直接匹配這個函數 cout << endl;
}
template <class T, class ...Args>
void ShowList(T x, Args... args)
{cout << x << " ";// args是N個參數的參數包 // 調?ShowList,參數包的第?個傳給x,剩下N-1傳給第?個參數包 ShowList(args...);
}
// 編譯時遞歸推導解析參數 
template <class ...Args>
void Print(Args... args)
{ShowList(args...);//注意傳參的時候三個點又寫到后面了
}
int main()
{Print();Print(1);Print(1, "xxxxx");Print(1, "xxxxx", 2.2);return 0;
}

? ? ? ? 我們把參數傳給print,然后print在調用showlist,每次“剔除”一個變量。知道參數包中變量數為0,此時再去調用showlist直接調用最上面的showlist函數。

? ? ? ? 正因為是編譯時調用,所以我們不能這樣寫:?

template <class T, class ...Args>
void ShowList(T x, Args... args)
{if (sizeof...(args) == 0){return;}cout << x << " ";// args是N個參數的參數包 // 調?ShowList,參數包的第?個傳給x,剩下N-1傳給第?個參數包 ShowList(args...);
}

? ? ? ? 因為這個函數結束條件是運行時判斷邏輯。?

? ? ? ? 還有一種包擴展的方式:

template <class T>
const T& GetArg(const T& x)
{cout << x << " ";return x;
}
template <class ...Args>
void Arguments(Args... args)
{cout << endl;
}
template <class ...Args>
void Print(Args... args)
{// 注意GetArg必須返回或者到的對象,這樣才能組成參數包給Arguments Arguments(GetArg(args)...);
}

? ? ? ? 我們把傳入print的幾個參數組成參數包傳給GetArg。對于GetArg來說,我們相當于把參數包中的每個參數都傳給GetArg,然后把GetArg的所有返回值在組成一個參數包,傳給Arguments.?這種包擴展就是普通的編譯推導,并不是遞歸。

? ? ? ? 需要注意的是上面這種的包擴展方式在vs上是倒序輸出的,也就是說輸出結果和我們的傳參順序恰好相反。造成這種結果的原因是C++標準沒有規定函數參數的求值順序,而大部分編譯器默認是從右到左。

? ? ? ? 1.3、emplace系列接口

? ? ? ? C++11之后所有的stl容器都新增了emplace接口,empalce系列的接口均為模板可變參數。接下來我們使用list來測試一下emplace系列的接口。

?????????

? ? ? ? 對于emplace_back和push_back來說,無論是傳左值,還是傳右值,效率都沒有區別。唯一有區別的場景就是這種:

int main()
{list<string> li;li.push_back("sss");//構造加移動構造li.emplace_back("sss");//直接構造return 0;
}

? ? ? ? 對于push_back來說,因為push_back這個函數的形參就是string&&類型的,如果我們想讓形參和實參匹配上,需要先對形參進行構造,構造一個臨時對象,然后再移動構造給string對象進行push_back。

? ? ? ? 但是對于emplace_back來說,由于他是一個模板,他可以直接接受const char*類型的變量,然后再在插入之前進行一次構造,構造出string對象進行插入就可以了。

? ? ? ? ?接下來我們看下面這個場景:

#include<iostream>
#include<list>
#include<string>
using namespace std;
int main()
{list<pair<int,double>> li;li.push_back({ 6,5.5 });//li.emplace_back({ 6,5.5 });li.emplace_back(6, 5.5);return 0;
}

?????????對于pair類型,我們使用emplace接口時不能傳花括號,也就是說不能傳初始化列表。因為咱們的emplace_back是模板函數,所以在傳參是他會去推導類型。由于initializer_list在推導時必須是initializer_list<T>,也就是說列表中的值類型必須是相同的。但是這里一個int,一個double不是同一類型參數,所以說會編譯報錯。

? ? ? ? 但是對于直接傳構造底層對象的參數是沒有問題的。這也是emplace系列接口的正確用法:在插入值時向emplace系列接口中直接傳入構造底層存儲對象類型所需的參數。這樣相比普通插入效率更高。但如果容器存儲的是一些基本類型(如int,double,char)時使用emplace_back或push_back效率上沒有差異。

? ? ? ? 現在我們對之前模擬實現的list進行一下升級,寫一下emplace系列接口:

......
list_node(T&& x):_next(nullptr), _prev(nullptr), _data(std::move(x))
{}
//如果上面兩個構造函數都給了默認值=T(),那么當new node時,也就是不傳參是無法確定匹配哪個構造函數。
//一般右值版本不給默認值。右值引用通常用于移動語義,而默認構造的臨時對象(T())不適合被移動。
// 語義上矛盾:右值引用表示要"竊取"資源,但默認參數會創建一個新對象
template<class ...Args>
list_node(Args&&... args): _next(nullptr), _prev(nullptr), _data(std::forward<Args>(args)...)
{}
......
template<class ...Args>
void emplace_back(Args&&... args)
{emplace(end(), std::forward<Args>(args)...);
}
template<class ...Args>
void emplace(iterator pos, Args&&... args)
{Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(std::forward<Args>(args)...);// prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;++_size;
}

?????????注意的是我們的參數包構造不需要解析參數包,也就是說編譯器不需要進行包擴展什么的,編譯器不需要一個一個看都是什么類型,而是直接拿去和list底層存儲類型的構造函數匹配。如果匹配對不上就報錯。

? ? ? ? 另外就是每次傳參數包我們都需要完美轉發。讓參數包中的參數都保持原有屬性。? ? ? ? ? ? ? ? ?

2、類的新功能

? ? ? ? 2.1、默認的移動構造和移動賦值

? ? ? ? C++11新增了兩個默認成員函數:移動構造函數和移動賦值運算符重載。

? ? ? ? 如果你沒有自己生成移動構造函數,且沒有實現析構函數,拷貝構造,拷貝賦值重載中的任意一個,那么編譯器就會自動生成一個默認移動構造。默認生成的移動構造函數,對于內置類型成員會執行逐成員按字節拷貝,自定義類型成員,則需要看這個成員是否實現移動構造,如果實現了就調用移動構造,沒有實現就調用拷貝構造。

? ? ? ? 同理,如果你沒有自己實現移動賦值重載函數,且沒有實現析構函數、拷貝構造、拷貝賦值重載中的任意一個,那么編譯器會自動生成一個默認移動賦值。默認生成的移動構造函數,對于內置類型成員會 執行逐成員按字節拷貝,自定義類型成員,則需要看這個成員是否實現移動賦值,如果實現了就調用移動賦值,沒有實現就調用拷貝賦值。

????????如果你提供了移動構造或者移動賦值,編譯器不會自動提供拷貝構造和拷貝賦值。

? ? ? ? 2.2、委托構造

class A
{
public:A(int a, int b):_a(a),_b(b){}A(int a, int b, char c):A(a, b){_c = c;}
private:int _a;int _b;char _c=0;
};

? ? ? ? 上面這個類就實現了委托構造,本質上也是一種復用。?

? ? ? ? 2.3、default和delete

? ? ? ? default可以強制讓編譯器生成默認函數:

class MyClass {
public:MyClass() = default; // 顯式要求編譯器生成默認構造函數MyClass(const MyClass&) = default; // 默認拷貝構造函數
};

? ? ? ? delete就與default相反,不讓編譯器生成某個默認函數:

class NonCopyable {
public:NonCopyable(const NonCopyable&) = delete;NonCopyable& operator=(const NonCopyable&) = delete;
};

? ? ? ? ?好了,今天的內容就分享到這,我們下期再見!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/908541.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/908541.shtml
英文地址,請注明出處:http://en.pswp.cn/news/908541.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

單元測試與QTestLib框架使用

一.單元測試的意義 在軟件開發中&#xff0c;單元測試是指對軟件中最小可測試單元&#xff08;通常是函數、類的方法&#xff09;進行隔離的、可重復的驗證。進行單元測試具有以下重要意義&#xff1a; 1.提升代碼質量與可靠性&#xff1a; 早期錯誤檢測&#xff1a; 在開發…

(附實現代碼)Step-Back 回答回退策略擴大檢索范圍

1. LangChain 少量示例提示模板 在與 LLM 的對話中&#xff0c;提供少量的示例被稱為 少量示例&#xff0c;這是一種簡單但強大的指導生成的方式&#xff0c;在某些情況下可以顯著提高模型性能&#xff08;與之對應的是零樣本&#xff09;&#xff0c;少量示例可以降低 Prompt…

16-Oracle 23 ai-JSON-Relational Duality-知識準備

一直做DBA的小伙伴&#xff0c;是不是對開發相對陌生一些。JSON 關系二元性是 Oracle Database 23ai 中重要的特性&#xff0c;同時帶來的是范式革命。JSON關系二元性解決了數據庫領域的根本矛盾?&#xff0c;結構化數據的嚴謹性與半結構化數據的靈活性之間的矛盾。 JSON Rela…

什么是預訓練?深入解讀大模型AI的“高考集訓”

1. 預訓練的通俗理解&#xff1a;AI的“高考集訓” 我們可以將預訓練&#xff08;Pre-training&#xff09; 形象地理解為大模型AI的“高考集訓”。就像學霸在高考前需要刷五年高考三年模擬一樣&#xff0c;大模型在正式誕生前&#xff0c;也要經歷一場聲勢浩大的“題海戰術”…

思爾芯攜手Andes晶心科技,加速先進RISC-V 芯片開發

在RISC-V生態快速發展和應用場景不斷拓展的背景下&#xff0c;芯片設計正面臨前所未有的復雜度挑戰。近日&#xff0c;RISC-V處理器核領先廠商Andes晶心科技與思爾芯&#xff08;S2C&#xff09;達成重要合作&#xff0c;其雙核單集群AX45MPV處理器已在思爾芯最新一代原型驗證系…

vscode配置lua

官網下載lua得到如下 打開vscode的擴展下載如下三個 打開vscode的此處設置 搜索 executorMap&#xff0c;并添加如下內容

理解 RAG_HYBRID_BM25_WEIGHT:打造更智能的混合檢索增強生成系統

目錄 理解 RAG_HYBRID_BM25_WEIGHT&#xff1a;打造更智能的混合檢索增強生成系統 一、什么是 Hybrid RAG&#xff1f; 二、什么是 RAG_HYBRID_BM25_WEIGHT&#xff1f; 三、參數設置示例 四、什么時候該調整它&#xff1f; 五、實戰建議 六、總結 理解 RAG_HYBRID_BM25…

Spring Boot 2 中 default-autowire 的使用

Spring Boot 2 中 default-autowire 的使用 在 Spring Boot 2 中&#xff0c;default-autowire 這個來自傳統 XML 配置的概念仍然存在&#xff0c;但它的使用已經大大減少&#xff0c;因為現代 Spring Boot 應用主要使用注解驅動的配置方式。 default-autowire 在 Spring Boo…

Spring Boot + Thymeleaf 防重復提交

在 Spring Boot 與 Thymeleaf 結合的 Web 應用中&#xff0c;防止重復提交可以采用token 機制 客戶端禁用按鈕的方式實現&#xff0c;在高并發場景下&#xff0c;考慮使用 Redis 存儲 token 而非 Session。 第一步&#xff1a;后端實現 Controller public class FormControl…

【20250607接單】Spark + Scala + IntelliJ 項目的開發環境配置從零教學

本教程適用于零基礎、一臺剛裝好 Windows 的全新電腦開始&#xff0c;搭建能運行 Spark Scala IntelliJ 項目的開發環境。以下是超詳細、小白級別逐步教程&#xff0c;從“下載什么”到“點擊哪里”都幫你列清楚。 &#x1f3af; 目標 操作系統&#xff1a;Windows10/11工具…

【ubuntu】虛擬機安裝配置,sh腳本自動化,包含 apt+時間同步+docker+mysql+redis+pgsql

可以說是ubuntu基礎環境搭建合集&#xff0c;個人學習用&#xff0c;使用sh一鍵安裝&#xff0c;避免復制各種命令 流程主要包括 0. 可選擇不同ubuntu版本對應安裝&#xff08;支持 Ubuntu 20.04/22.04/23.04/24.04&#xff09; 1. apt換源aliyun 2. 時間選擇上海時區&#x…

Rust 學習筆記:關于智能指針的練習題

Rust 學習筆記&#xff1a;關于智能指針的練習題 Rust 學習筆記&#xff1a;關于智能指針的練習題問題一問題二問題三問題四問題五問題六問題七問題八問題九問題十問題十一 Rust 學習筆記&#xff1a;關于智能指針的練習題 參考視頻&#xff1a; https://www.bilibili.com/vi…

JavaScript ES6 解構:優雅提取數據的藝術

JavaScript ES6 解構&#xff1a;優雅提取數據的藝術 在 JavaScript 的世界中&#xff0c;ES6&#xff08;ECMAScript 2015&#xff09;的推出為開發者帶來了許多革命性的特性&#xff0c;其中“解構賦值”&#xff08;Destructuring Assignment&#xff09;無疑是最受歡迎的功…

Shell 命令及運行原理 + 權限的概念(7)

文章目錄 Shell 命令以及運行原理&#xff08;4-1.22.08&#xff09;Linux權限的概念1. 什么是權限2. 認識人&#xff08;普通用戶&#xff0c;root用戶&#xff09;以及兩種用戶的切換認識普通用戶和root用戶兩種用戶之間的切換指令提權 3. 文件的屬性解析 權限屬性指令ll顯示…

以智能管理為基礎,樓宇自控打造建筑碳中和新路徑

在全球氣候變化的嚴峻形勢下&#xff0c;“碳中和”已成為各國發展的重要戰略目標。建筑行業作為能源消耗與碳排放的“大戶”&#xff0c;其運行階段的能耗占全社會總能耗近40%&#xff0c;碳排放占比與之相當&#xff0c;實現建筑碳中和迫在眉睫。傳統建筑管理模式下&#xff…

Python爬蟲實戰:研究Hyper 相關技術

一、項目概述 本項目展示了如何結合 Python 的異步編程技術與 Hyper 框架開發一個高性能、可擴展的網絡爬蟲系統。該系統不僅能夠高效地爬取網頁內容,還提供了 RESTful API 接口,方便用戶通過 API 控制爬蟲的運行狀態和獲取爬取結果。 二、系統架構設計 1. 整體架構 系統采…

html 滾動條滾動過快會留下邊框線

滾動條滾動過快時&#xff0c;會留下邊框線 但其實大部分時候是這樣的&#xff0c;沒有多出邊框線的 滾動條滾動過快時留下邊框線的問題通常與滾動條樣式和滾動行為有關。這種問題可能出現在使用了自定義滾動條樣式的情況下。 注意&#xff1a;使用方法 6 好使&#xff0c;其它…

【Linux】Ubuntu 創建應用圖標的方式匯總,deb/appimage/通用方法

Ubuntu 創建應用圖標的方式匯總&#xff0c;deb/appimage/通用方法 對于標準的 Ubuntu&#xff08;使用 GNOME 桌面&#xff09;&#xff0c;desktop 后綴的桌面圖標文件主要保存在以下三個路徑&#xff1a; 當前用戶的桌面目錄&#xff08;這是最常見的位置&#xff09;。所…

【自然語言處理】大模型時代的數據標注(主動學習)

文章目錄 A 論文出處B 背景B.1 背景介紹B.2 問題提出B.3 創新點 C 模型結構D 實驗設計E 個人總結 A 論文出處 論文題目&#xff1a;FreeAL: Towards Human-Free Active Learning in the Era of Large Language Models發表情況&#xff1a;2023-EMNLP作者單位&#xff1a;浙江大…

【論文解讀】DeepSeek-R1

文章目錄 概覽一、DeepSeek-R1-Zero&#xff1a;在 Base Model 上直接進行 RL&#xff08;一&#xff09;強化學習算法&#xff08;二&#xff09;獎勵模型&#xff08;三&#xff09;數據構造&#xff08;四&#xff09;DeepSeek-R1-Zero 的性能、自我進化過程和 Aha Moment1.…