作者:taoklin,騰訊WXG後台開發
一、簡單特性1. namespace 嵌套c 17使我們可以更加簡潔使用命名空間:
2. std::variant 升級版的C語言Union
在C 17之前,通常使用Union來定義一個可以存儲不同類型的變量,現在可以通過std::variant<T1,T2,...> 來定義一個可以存儲不同類型的新變量。
std::variant優勢在于:①存儲了變量的類型信息,更安全;②可以存儲複雜對象,更好用。
用法如下
3. [[fallthrough]] 顯式說明某個switch分支無需break
如果在寫代碼時遇到在swtich語句中需要執行完case 1,繼續執行case 2的情況,可以使用[[fallthrough]],此時編譯器會忽略此處break語句檢查,還能顯式的告知Code Reviewer 此處是有意不寫break語句。
用法如下:
4. [[nodiscard]] 顯式說明不能忽略函數返回值
如果我們編寫的某個函數不希望調用時忽略它的返回值,那麼可以在函數聲明處,使用[[nodiscard]]修飾這個函數。
用法如下:
二、std::optional 更優雅地編寫可能無返回結果的函數用法:
使用std::optional<T>來修飾函數返回值,表明這個函數可能不會返回值,T代表原有的返回類型。具體使用方法見示例函數TestOptionalInt:
用途:
過去當我們編寫一個獲取目标值的函數時,如果這個函數在某些情況下不能返回目标值,那麼我們就必須通過兩個參數去獲得目标值:一個參數來表明是否存在目标值,另一個參數返回目标值;或者是在函數無返回值時抛出異常。下面以FindUserName函數來展示C 17之前的幾種實現途徑。
引入C 17的std::optional<T>,我們可以更優雅更安全的編寫FindUserName函數。
三、std::string_view 字符串視圖用法:
C 17引入的std::string_view來協助程序員更高效的使用隻讀字符串,初始化std::string_view時需要傳入已有的字符串。作為函數參數時隻使用值拷貝形式,即std::string_view;不要引用字符串視圖,即:std::string_view&。
std::string_view本質上是持有一個字符串的指針,因此需要保證:①被持有的字符串生命周期比std::string_view變量長;②被持有的字符串在std::string_view變量生命周期結束之前,保持不變。
用途:當遇到需要使用隻讀字符串,尤其是傳入隻讀字符串作為函數參數時,優先使用std::string_view。在以下兩個場景使用string_view比使用 const string&更好。
适用場景1:可能傳入隻讀C風格字符串參數時C風格字符串是指: const char* str = "C風格字符串"
char* str = "C風格字符串"
char[] str_array = "C風格字符串"
以一個字符串打印函數StringDisplay為例,下面的代碼是常規的編寫方法。
如果我們在函數中使用const std::string&類型的輸入參數,當我們傳入C風格字符串時,那麼首先要生成一個string對象,此時帶來了額外的拷貝操作。如果我們要避免這樣的情況發生,那麼我們就必須把入參設置為const char*類型,但是這是C風格字符串類型,調用此類函數是很麻煩的,如下所示:
但如果我們使用std::string_view,那麼這些問題都可以解決,如下面的代碼所示:
适用場景2:要進行string.substr()操作時:
對字符串進行處理是一個很常見的業務場景,如果我們需要從字符串中提取某些字段,使用std::string_view是一個非常好的選擇。
以一個字符串分割函數StringSplit為例,下面代碼是常規編寫方法:
在上述代碼中,每分割完一次字符串,都需要把已經分割完的部分去掉,但是我們不能改變原字符串,因此隻能拷貝一個新的字符串傳入下一次遞歸中。但是如果使用字符串視圖:std::string_view,那麼可以改變字符串視圖然後傳入下一次遞歸中,因為改變字符串視圖是不會改變原字符串的,從而避免一次了字符串拷貝,代碼示例如下:
不适用場景:函數内部要調用C風格字符串為參數的子函數
std::string_view 并不是完美的,大部分時候,我們都可以使用std::string_view 替代const std::string&,我們可以把std::string_view理解成,一種同時擁有C語言的const char*的指針拷貝成本和 C 語言中std::string類大部分api的類型。但與std::string相比,std::string_view不提供c_str()函數,因為std::string_view具有以下缺點:
四、if constexpr:按條件編譯用法:
if constexpr語句是編譯期的if判斷語句,if constexpr要求後面的判斷條件是一個編譯期可以确定的常量。
用途:用于編寫需要進行編譯期判斷的函數,簡化模版函數的書寫。
适用場景1:簡化模版偏特化的寫法編寫模版函數時,有時需要對某些類型進行特殊處理,此時就需要寫模闆偏特化函數。比如下列代碼展示的Convert函數的例子:
在C 17裡面,可以直接簡化成一個函數:
适用場景2:編寫變參模版函數
在C 17之前,如果要編寫一個變參模闆函數,那麼必須額外寫一個函數處理入參數量最少時候的特例,下面以Sum函數為例:
在C 17中,可以這樣編寫:
使用場景3:替代enable_if
編寫模闆函數時,經常要使用enable_if語句來進行靜态類型檢查,保證模闆輸入的類型滿足某種要求,例如在下列的判斷一個數是奇數還是偶數的IsOdd函數中,該函數通過enable_if語句限定了輸入類型隻能是整數。
使用C 17可以使用更易懂的方法實現編譯期類型檢查:
錯誤用法:
if constexpr語句中,不能将else分支移到判斷語句外面,例如下列的判斷是不是整數的Convert函數的編寫方法就是錯誤的。
五、if及switch初始化語句用法:
c 17支持在if和switch的判斷語句之前增加一個初始化語句,可以用來初始化作用域僅為if或switch語句内的變量,有助于提升代碼的可讀性和正确性。
用途:六、結構化綁定用法:
auto [key, value] = std::make_pair<int, std::string>(1, "名字");
auto& [key, value] = std::make_pair<int, std::string>(1, "名字");
auto&& [key, value] = std::make_pair<int, std::string>(1, "名字");
用途:以下特性僅做記錄,個人觀點不是非常推薦使用。
1.構造函類型推導:模闆類初始化可以不顯示指定類型在C 17之前,模版類的構造函數在調用時必須指明類型,不能進行隐式類型推導;但是調用普通模版函數時是可以不顯式指明類型的,這是因為普通模闆函數可以進行隐式類型推導,下面代碼以pair、tuple和vector為例展示了這一現象:
在C 17之後,模闆類的構造函數也可以進行隐式類型推導:
個人觀點:構造函數中進行隐式類型推導,這會讓人擔憂可能存在難以發現的類型推導錯誤。下面的例子裡,程序員原意是新建一個std::pair<int, std::string>類型,但是編譯器自動推導出的是std::pair<int, const char*>類型。
這段代碼最後會輸出error type。以上代碼是筆者使用CLion編譯器時的截圖,但是如果使用筆者常用的Vs Code編輯器,并不能像使用CLion實時獲知int_string_pair_a和int_string_pair_c這兩個變量的隐式類型推導結果與程序員原意不符。
2. std::any 可以存任意可拷貝類型變量的容器C 17使用了std::any來替代C語言中的void*,std::any有以下優點
用法如下所示:
個人觀點:使用std::any變量,意味着可能存在動态類型識别,這讓人沒有安全感。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!