日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

C 语言中std::array的神奇用法总结

發布時間:2023/12/2 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C 语言中std::array的神奇用法总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

std::array是在C 11標準中增加的STL容器,它的設計目的是提供與原生數組類似的功能與性能。也正因此,使得std::array有很多與其他容器不同的特殊之處,比如:std::array的元素是直接存放在實例內部,而不是在堆上分配空間;std::array的大小必須在編譯期確定;std::array的構造函數、析構函數和賦值操作符都是編譯器隱式聲明的……這讓很s多用慣了std::vector這類容器的程序員不習慣,覺得std::array不好用。

但實際上,std::array的威力很可能被低估了。在這篇文章里,我會從各個角度介紹下std::array的用法,希望能帶來一些啟發。

本文的代碼都在C 17環境下編譯運行。當前主流的g 版本已經能支持C 17標準,但是很多版本(如gcc 7.3)的C 17特性不是默認打開的,需要手工添加編譯選項-std=c 17。

自動推導數組大小

很多項目中都會有類似這樣的全局數組作為配置參數:

uint32_t?g_cfgPara[]?=?{1,?2,?5,?6,?7,?9,?3,?4};

當程序員想要使用std::array替換原生數組時,麻煩來了:

array?g_cfgPara?=?{1,?2,?5,?6,?7,?9,?3,?4};??//?注意模板參數“8”

程序員不得不手工寫出數組的大小,因為它是std::array的模板參數之一。如果這個數組很長,或者經常增刪成員,對數組大小的維護工作恐怕不是那么愉快的。有人要抱怨了:std::array的聲明用起來還沒有原生數組方便,選它干啥?

但是,這個抱怨只該限于C 17之前,?C 17帶來了類模板參數推導特性,?你不再需要手工指定類模板的參數:

array?g_cfgPara?=?{1,?2,?5,?6,?7,?9,?3,?4};??//?數組大小與成員類型自動推導

看起來很美好,但很快就會有人發現不對頭:數組元素的類型是什么?還是std::uint32_t嗎?


有人開始嘗試只提供元素類型參數,讓編譯器自動推導長度,遺憾的是,它不會奏效。

array?g_cfgPara?=?{1,?2,?5,?6,?7,?9,?3,?4};??//?編譯錯誤

好吧,暫時看起來std::array是不能像原生數組那樣聲明。下面我們來解決這個問題。

用函數返回std::array

問題的解決思路是用函數模板來替代類模板——因為C 允許函數模板的部分參數自動推導——我們可以聯想到std::make_pair、std::make_tuple這類輔助函數。巧的是,?C 標準真的在TS v2試驗版本中推出過std::make_array,?然而因為類模板參數推導的問世,這個工具函數后來被刪掉了。


但顯然,用戶的需求還是存在的。于是在C 20中,?又新增了一個輔助函數std::to_array。

別被C 20給嚇到了,這個函數的代碼其實很簡單,我們可以把它拿過來定義在自己的C 17代碼中[1]。

template<typename R, typename P, size_t N, size_t... I>constexpr array to_array_impl(P (&a)[N], std::index_sequence) noexcept{ ? ?return { {a[I]...} };} template<typename T, size_t N>constexpr auto to_array(T (&a)[N]) noexcept{ ? ?return to_array_impl<std::remove_cv_t, T, N>(a, std::make_index_sequence{});} template<typename R, typename P, size_t N, size_t... I>constexpr array to_array_impl(P (&&a)[N], std::index_sequence) noexcept{ ? ?return { {move(a[I])...} };} template<typename T, size_t N>constexpr auto to_array(T (&&a)[N]) noexcept{ ? ?return to_array_impl<std::remove_cv_t, T, N>(move(a), std::make_index_sequence{});}

細心的朋友會注意到,上面這個定義與C 20的推薦實現有所差異,這是有目的的。稍后我會解釋這么干的原因。

現在讓我們嘗試下用新方法解決老問題:

auto?g_cfgPara?=?to_array({1,?2,?5,?6,?7,?9,?3,?4});??//?類型不是uint32_t?

不對啊,為什么元素類型不是原來的std::uint32_t?


這是因為模板參數推導對std::initializer_list的元素拒絕隱式轉換,如果你把to_array的模板參數從int改為uint32_t,會得到如下編譯錯誤:

D:\Work\Source_Codes\MyProgram\VSCode\main.cpp:51:61:?error:?no?matching?function?for?call?to?'to_array(<brace-enclosed?initializer?list>)'?auto?g_cfgPara?=?to_array({1,?2,?5,?6,?7,?9,?3,?4});D:\Work\Source_Codes\MyProgram\VSCode\main.cpp:34:16:?note:?candidate:?'template<class?T,?long?long?unsigned?int?N>?constexpr?auto?to_array(T?(&)[N])'?constexpr?auto?to_array(T?(&a)[N])?noexcept????????????????^~~~~~~~D:\Work\Source_Codes\MyProgram\VSCode\main.cpp:34:16:?note:???template?argument?deduction/substitution?failed:D:\Work\Source_Codes\MyProgram\VSCode\main.cpp:51:61:?note:???mismatched?types?'unsigned?int'?and?'int'?auto?g_cfgPara?=?to_array({1,?2,?5,?6,?7,?9,?3,?4});D:\Work\Source_Codes\MyProgram\VSCode\main.cpp:46:16:?note:?candidate:?'template<class?T,?long?long?unsigned?int?N>?constexpr?auto?to_array(T?(&&)[N])'?constexpr?auto?to_array(T?(&&a)[N])?noexcept????????????????^~~~~~~~D:\Work\Source_Codes\MyProgram\VSCode\main.cpp:46:16:?note:???template?argument?deduction/substitution?failed:D:\Work\Source_Codes\MyProgram\VSCode\main.cpp:51:61:?note:???mismatched?types?'unsigned?int'?and?'int'

Hoho,有點慘是不,繞了一圈回到原點,還是不能強制指定類型。

這個時候,之前針對std::array做的修改派上用場了:我給to_array_impl增加了一個模板參數,讓輸入數組的元素和返回std::array的元素用不同的類型參數表示,這樣就給類型轉換帶來了可能。為了實現轉換到指定的類型,我們還需要添加兩個工具函數:

template<typename R, typename P, size_t N>constexpr auto to_typed_array(P (&a)[N]) noexcept{ ? ?return to_array_impl(a, std::make_index_sequence{});} template<typename R, typename P, size_t N>constexpr auto to_typed_array(P (&&a)[N]) noexcept{ ? ?return to_array_impl(move(a), std::make_index_sequence{});}

這兩個函數和to_array的區別是:它帶有3個模板參數:第一個是要返回的std::array的元素類型,后兩個和to_array一樣。這樣我們就可以通過指定第一個參數來實現定制std::array元素類型了。

auto?g_cfgPara?=?to_typed_array({1,?2,?5,?6,?7,?9,?3,?4});??//?自動把元素轉換成uint32_t

這段代碼可以編譯通過和運行,但是卻有類型轉換的編譯告警。當然,如果你膽子夠大,可以在to_array_impl函數中放一個static_cast來消除告警。但是編譯告警提示了我們一個不能忽視的問題:如果萬一輸入的數值溢出了怎么辦?

auto?g_a?=?to_typed_array({256,?-1});??//?數字超出uint8_t范圍

編譯器還是一樣的會讓你編譯通過和運行,g_a中的兩個元素的值將分別為0和255。如果你不明白為什么這兩個值和入參不一樣,你該復習下整型溢出與回繞的知識了。

顯然,這個方案還不完美。但我們可以繼續改進。

編譯期字面量數值合法性校驗

首先能想到的做法是在to_array_impl函數中放入一個if判斷之類的語句,對于超出目標數值范圍的輸入拋出異常或者做其他處理。這當然可行,但要注意的是這些工具函數是可以在運行期調用的,對于這種常用的基礎函數來說,性能至關重要。一旦在里面加入了錯誤判斷,意味著運行時的每一次調用性能都會下降。

理想的設計是:只有在編譯期生成的數組才進行校驗,并且報編譯錯誤。但運行時調用函數時不要加入任何校驗。

可惜的是,至少在C 20之前,沒有辦法指定函數只允許在編譯期執行[2]。那有沒有其他手段呢?

熟悉C 的人知道:C 的編譯期處理大多可以用模板的trick來完成——因為模板參數一定是編譯期常量。因此我們可以用模板參數來完成編譯期處理——只要把數組元素全部作為模板的非類型參數就可以了。當然,這里有個問題:模板的非類型參數的類型怎么確定?正好C 17提供了auto模板參數的功能,可以派上用場:

template<typename T>constexpr void CheckIntRanges() noexcept {} ?// 用于終結遞歸 template<typename T, auto M, auto... N>constexpr void CheckIntRanges() noexcept

總結

以上是生活随笔為你收集整理的C 语言中std::array的神奇用法总结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。