最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

TinyFormat 練習(xí)

2022-09-09 00:12 作者:Meriex  | 我要投稿

在前幾節(jié)對(duì) TinyFormat 源碼的閱讀過(guò)程中我們看到了很多關(guān)于宏和模板的使用方式,那其中給我們留下印象最深的應(yīng)該就是 FormatListN 使用變長(zhǎng)模板的初始化方式了,現(xiàn)在讓我們?cè)囍鴥?yōu)化它。

在原先的代碼中作者用了大量的中間類(lèi)和函數(shù)去適配一些 c 風(fēng)格的代碼,比如 FormatList 類(lèi)中以指針保存的數(shù)組首地址,以及內(nèi)置數(shù)組 m_formatterStore 等等,這使得整個(gè)初始化的過(guò)程過(guò)于復(fù)雜,從一組形如 1, std::string("test"), 2, 3.14159 的變長(zhǎng)參數(shù)到 FormatListN 初始化完成你需要經(jīng)過(guò)三步:

  1. 通過(guò) makeFormatList(args...) 確定參數(shù)的長(zhǎng)度 N 并傳入 FormatListN 的構(gòu)造函數(shù)

  2. FormatListN 中初始化 m_formatterStore ,并把數(shù)組首地址和長(zhǎng)度傳給父類(lèi) FormatList

  3. FormatArg 中將每個(gè)類(lèi)型的參數(shù)都轉(zhuǎn)換成 const void* 保存

得益于 c++17 的強(qiáng)大,我們可以完成以下優(yōu)化:

  1. 通過(guò) deduction guide 完成 makeFormatList 的工作

  2. 使用 std::variant 配合 std::vector 完成對(duì)不同類(lèi)型不同長(zhǎng)度參數(shù)的存儲(chǔ),而無(wú)需將其轉(zhuǎn)化為 const void*

那跟隨以上目標(biāo)的指引,我們優(yōu)化的 FormatList 類(lèi)實(shí)現(xiàn)如下:

沒(méi)錯(cuò)就是這么簡(jiǎn)單,讓我們來(lái)看一下 FormatList 的功能,首先 m_formatterStore 是一個(gè) std::vector ,這首先使得我們拋棄了指針并且無(wú)需考慮數(shù)組的長(zhǎng)度,邊界處理也更加方便,接著其儲(chǔ)存的 std::variant 是在 c++17 后引入的類(lèi)型,它可以接收除引用、數(shù)組和 void 以外的所有類(lèi)型變量,只要我們通過(guò) std::variant<Args...> 告訴它我們傳入?yún)?shù)的類(lèi)型范圍。接著在構(gòu)造函數(shù)中通過(guò) c++17 引入的折疊表達(dá)式依次通過(guò) vec.emplace_back(args) 將所有參數(shù)加入到 m_formatterStore 中。

調(diào)用方式如下:

現(xiàn)在看上去一切都很美好,但實(shí)際上這里存在一個(gè)問(wèn)題,那就是一旦我們?cè)趯?shí)例化時(shí)傳入的參數(shù)分屬同一個(gè)類(lèi)型例如:

那么我們將收獲一串來(lái)自編譯器的錯(cuò)誤提示:

接著配合簡(jiǎn)單的 Google 我們就會(huì)發(fā)現(xiàn)一個(gè)天坑 ——— 沒(méi)錯(cuò), std::variant 實(shí)例化時(shí)不允許傳入多個(gè)相同的類(lèi)型!

沒(méi)辦法,苦思多時(shí)無(wú)果后只能求助于 Google,于是我找到了來(lái)自 @Piotr Skotnicki 大佬的精彩回答:

這里首先包含三個(gè)東西:

  1. unique_variant_t 用于包裝一個(gè)包含重復(fù)類(lèi)型的 std::variant 并返回一個(gè)無(wú)重復(fù)版本,最后 ::type 取得 T 也就是 std::variant

  2. unique_variant 是一個(gè)模板類(lèi)聲明,其偏特化版本 unique_variant<std::variant<Ts...>> 接收多個(gè)類(lèi)型并通過(guò) filter_duplicates 完成去重

  3. filter_duplicates ,負(fù)責(zé)去重和返回類(lèi)型,也是我們接下來(lái)的重點(diǎn)

以一個(gè)如下的調(diào)用來(lái)舉例說(shuō)明:

這時(shí)我們知道 unique_variant_t 解析的類(lèi)型 T 實(shí)際上是 std::variant<int, char, int, int> 但是這里還沒(méi)有實(shí)際使用所以不必?fù)?dān)心,繼續(xù)往下走,我們嘗試獲取 ::type ,因此要先將 T 傳入 unique_variant 那么這里我們匹配到的就是它的偏特化版本,這里的 Ts... 就是 int, char, int, int ,那么再往后走傳遞 std::variant<>, int, char, int, intfilter_duplicates 中,這里可能會(huì)有疑惑說(shuō)那這樣一來(lái) T 不就是 std::variant<> ?了嗎,與我們期待的 std::variant<int, char> 并不相符啊,實(shí)際上不是的,這里因?yàn)槟0迤ヅ湟?guī)則的原因?qū)嶋H上找到的同樣是 filter_duplicates 的偏特化版本,此時(shí) Cstd::variant , Ts... 為空, Uint , Us...char, int, int ,接著借助 std::conditional_t 我們可以在編譯期間執(zhí)行不同的分支,對(duì)于 (std::is_same_v<U, Ts> || ...) 也就是將 UTs... 內(nèi)所有類(lèi)型逐個(gè)比較(這里同樣用到了折疊表達(dá)式展開(kāi)),如果有任意一個(gè)相同,那么其繼承的父類(lèi)為 filter_duplicates<C<Ts...>, Us...> 實(shí)際上也就是 std::variant 內(nèi)的 Ts... 不變,重復(fù)的 U 被丟棄;如果都不相同那么將其加入 Ts.. 中。

最后這里也非常巧妙,要注意偏特化/特化版本的類(lèi)實(shí)現(xiàn)與原類(lèi)是完全獨(dú)立的,因此在向 Ts... 中不斷添加類(lèi)型的過(guò)程中整個(gè)類(lèi)是沒(méi)有 unique_variant_t 所需要的 ::type 調(diào)用的,而到最后所有類(lèi)型都被拋棄或加入到 std::variant 后, Us... 為空無(wú)法再匹配到偏特化版本,因此匹配到原版本,此時(shí) Tstd::variant<int, char> , Ts... 為空,這時(shí)類(lèi)中的 using type = T; 才被真正聲明并一層層繼承下來(lái)。

這樣一來(lái)我們就完成了對(duì) FormatList 類(lèi)的基本優(yōu)化,對(duì) m_formatterStore 的取用通過(guò) visit 也可以輕松完成。

下次再會(huì)!


TinyFormat 練習(xí)的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
肥乡县| 石柱| 尉犁县| 吴川市| 紫金县| 莱州市| 阿荣旗| 界首市| 都匀市| 德昌县| 那曲县| 安宁市| 常州市| 隆安县| 山丹县| 怀宁县| 出国| 永济市| 汝州市| 新平| 来凤县| 应用必备| 渭源县| 吴桥县| 临朐县| 乐亭县| 崇阳县| 石景山区| 蕲春县| 乐都县| 光山县| 永登县| 扶余县| 南澳县| 西和县| 苏尼特右旗| 政和县| 衡东县| 内黄县| 阿拉善右旗| 盈江县|