欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++17使用折疊表達(dá)式實(shí)現(xiàn)一個(gè)IsAllTrue函數(shù)的過(guò)程

 更新時(shí)間:2024年09月09日 08:50:10   作者:七昂的技術(shù)之旅  
本文介紹了利用C++17特性實(shí)現(xiàn)IsAllTrue函數(shù)的方法,詳細(xì)講解了從基于初始化列表的初級(jí)版本到使用折疊表達(dá)式和類型萃取的高級(jí)優(yōu)化版本,需要的朋友參考下吧

前言

讓我們實(shí)現(xiàn)一個(gè) IsAllTrue 函數(shù),支持變長(zhǎng)參數(shù),可傳入多個(gè)表達(dá)式,必須全部計(jì)算為true,該函數(shù)才返回true。

本文記錄了逐步實(shí)現(xiàn)與優(yōu)化該函數(shù)的思維鏈,用到了以下現(xiàn)代C++新特性知識(shí),適合對(duì)C++進(jìn)階知識(shí)有一定了解的人。這樣一種從實(shí)際問(wèn)題來(lái)學(xué)習(xí)和運(yùn)用知識(shí)的過(guò)程還是挺有趣的,特此整理分享一下。

  • 可變長(zhǎng)參數(shù)模板 (C++11)
  • 折疊表達(dá)式 (C++17)
  • 條件編譯 if constexpr (C++17)
  • 類型萃取 type traits (C++11)
  • 完美轉(zhuǎn)發(fā)std::forward (C++11)
  • 結(jié)構(gòu)化綁定 std::bind (C++11)

初級(jí)版本——基于初始化列表實(shí)現(xiàn)

可以使用初始化列表 std::initializer_list 存儲(chǔ)多個(gè)bool變量,實(shí)現(xiàn)傳入多個(gè)bool值的目的,這種方法實(shí)際上該函數(shù)只有一個(gè)參數(shù),實(shí)現(xiàn)如下:

bool IsAllTrue(const std::initializer_list<bool>& conditions) {
    return std::all_of(conditions.begin(), conditions.end(), [](const bool a) {
        return a;
    });
}

使用方法如下:

int a = 1;
bool b = true;
auto c = []() {return true;}
IsAllTrue({a, b, c});

這個(gè)方法的實(shí)現(xiàn)簡(jiǎn)單易用,但是對(duì)于代碼有更高追求的人并不滿足于此,以上實(shí)現(xiàn)存在如下問(wèn)題:

  • 傳入?yún)?shù)是一個(gè)初始化列表,需要寫大括號(hào){},不夠優(yōu)雅。
  • 調(diào)用函數(shù)前計(jì)算了每一個(gè)條件表達(dá)式,但實(shí)際任意一個(gè)為false,即可返回,可能存在如下問(wèn)題:

    不必要的函數(shù)調(diào)用帶來(lái)一定計(jì)算開(kāi)銷;

    當(dāng)前后表達(dá)式存在依賴關(guān)系時(shí),比如 p && p →a ,如果p是指針且為空, 計(jì)算p→a 會(huì)導(dǎo)致程序崩潰。

對(duì)于不了解這個(gè)函數(shù)用法的人而言,使用這個(gè)實(shí)現(xiàn)是會(huì)存在一定風(fēng)險(xiǎn)的。所以我們需要想辦法利用 && 實(shí)現(xiàn)短路求值,以及對(duì)函數(shù)結(jié)果的延遲計(jì)算。

進(jìn)階版本——基于折疊表達(dá)式實(shí)現(xiàn)

折疊表達(dá)式(Fold expressions)

折疊表達(dá)式是C++17引入的新特性,可通過(guò)二元操作符折疊可變長(zhǎng)參數(shù)模板中的參數(shù)包。這個(gè)特性的引入是為了簡(jiǎn)化C++11可變長(zhǎng)參數(shù)模板的使用。

  • 根據(jù)左右方向可分為左折疊右折疊

一元左折疊(Unary right fold)和一元右折疊(Unary left fold)形式如下:

( pack op... )  //一元右折疊,從右往左計(jì)算, 等同于(E1 op (... op (EN-1 op EN)))
( ... op pack ) //一元左折疊,從左往右計(jì)算, 等同于(((E1 op E2) op ...) op EN)

在大多數(shù)情況下,對(duì)于交換律成立的操作符(如 + 和 *),左折疊和右折疊的結(jié)果是相同的。然而,對(duì)于非交換的操作符,結(jié)果可能不同,例如減法或除法。

  • 根據(jù)是否有初始值可分為一元二元

二元折疊表達(dá)式分為:二元右折疊(Binary right fold)和 二元左折疊(Binary left fold)。

( pack op ... op init )	 //二元右折疊
( init op ... op pack )	 //二元左折疊
  • 使用二元左折疊的例子
template<typename... Args>
void printer(Args&&... args)
{
     ((std::cout<< args << " "), ...)<< "\n";
}

基于一元右折疊的IsAllTrue函數(shù)

基于 &&運(yùn)算符的一元右折疊(Unary right fold)實(shí)現(xiàn)IsAllTrue如下:

template<typename... Args>
bool IsAllTrue(Args... args) { 
	return (std::forward<Args>(args) && ...); 
}
  • 注:折疊表達(dá)式的最外層括號(hào)是必須的。

但以上實(shí)現(xiàn),該模板本質(zhì)上仍只能支持變長(zhǎng)的多個(gè)bool參數(shù),這會(huì)導(dǎo)致先計(jì)算出bool值再傳入,仍未實(shí)現(xiàn)函數(shù)結(jié)果的延遲計(jì)算。

使用type traits 進(jìn)一步優(yōu)化

如何可以實(shí)現(xiàn)延遲計(jì)算呢?首先我們可以明確下,傳遞給該函數(shù)的參數(shù)類型,可能是bool值、可以計(jì)算出bool值的表達(dá)式或可調(diào)用對(duì)象、可轉(zhuǎn)換為bool值的指針和數(shù)值。

總體可分為兩類,一類是可轉(zhuǎn)換為bool的表達(dá)式,另一類是可計(jì)算出bool的可調(diào)用對(duì)象。

由于參數(shù)類型(bool、函數(shù)對(duì)象、指針等)和類型特征(是否可調(diào)用、是否可以轉(zhuǎn)成bool)均是可以在編譯期確定的。

為了避免在編譯期把模板參數(shù)類型都推斷為bool,可定義 IsTrue 函數(shù)模板定義表達(dá)式bool值的計(jì)算方式,使模板可以推斷出原表達(dá)式自身的類型,從而可以延遲其計(jì)算過(guò)程。其中用到了編譯期條件if constexpr 和 一種類型萃取是否可調(diào)用 std::is_invocable_v ,這兩個(gè)均是C++17引入的特性。

如果具備可調(diào)用的特征,則進(jìn)行函數(shù)調(diào)用并返回結(jié)果;否則,將其轉(zhuǎn)換為bool值返回。實(shí)現(xiàn)如下:

template <typename T>
bool IsTrue(T&& value) {
    if constexpr (std::is_invocable_v<T>) {
        // 如果是可調(diào)用對(duì)象,調(diào)用它并返回結(jié)果
        return std::forward<T>(value)();
    } else {
        // 否則,將其轉(zhuǎn)換為bool
        return static_cast<bool>(std::forward<T>(value));
    }
}

基于以上模板改寫 IsAllTure 模板函數(shù) :

template <typename... Args>
bool IsAllTrue(Args&&... args) {
    return (IsTrue(std::forward<Args>(args)) && ...);
}

該實(shí)現(xiàn)的本質(zhì)是我們希望在用N個(gè)表達(dá)式傳入該模板函數(shù)后,模板實(shí)例化為形同如下形式,從而可以實(shí)現(xiàn)短路機(jī)制:

static_cast<bool>(Expr1) && Expr2() && static_cast<bool>(Expr3) && ... && ExprN()

函數(shù)測(cè)試

對(duì)以上代碼進(jìn)行如下測(cè)試,注釋為輸出結(jié)果,可以看到,能夠滿足我們的需求:

auto lambdaTrue = []() { 
    std::cout<<" lambda true"<<std::endl;
    return true; 
};
auto lambdaFalse = []() { 
    std::cout<<" lambda false"<<std::endl;
    return false; 
};
class Foo  {
public:
    int a;
};
Foo* p = nullptr;
IsAllTrue(true, lambdaTrue);  // 輸出lambda true
IsAllTrue(false, lambdaTrue); // 無(wú)輸出,實(shí)現(xiàn)了短路機(jī)制以及延遲計(jì)算
IsAllTrue(p, p->a);  // 正常運(yùn)行,不會(huì)coredump

以上為了方便,均使用定義了無(wú)參lambda函數(shù)進(jìn)行了測(cè)試。為了延遲一般含參函數(shù)的計(jì)算結(jié)果,能夠方便傳入帶參數(shù)的函數(shù)對(duì)象,還可以基于std::bind實(shí)現(xiàn)一個(gè)用于生成可調(diào)用對(duì)象的函數(shù):

template <typename F, typename... Args>
auto make_callable(F&& f, Args&&... args) {
    return std::bind(std::forward<F>(f), std::forward<Args>(args)...);
}

CPP 復(fù)制 全屏

比如:

bool less(int a, int b) {
	return a < b;
}
IsAllTrue(true, make_callable(less, 1, 2));

完整測(cè)試代碼:https://compiler-explorer.com/z/fTvq7Y36Y

知識(shí)總結(jié)

本文使用了以下C++知識(shí)實(shí)現(xiàn)了一個(gè)高效的IsAllTrue函數(shù),優(yōu)點(diǎn)為它的使用安全且較為高效,缺點(diǎn)在于代碼實(shí)現(xiàn)較為復(fù)雜,對(duì)C++知識(shí)掌握程度要求較高,過(guò)多使用也會(huì)導(dǎo)致代碼體積膨脹。

  • 條件編譯if constexpr :
    • 這個(gè)關(guān)鍵字用于在編譯時(shí)判斷是否滿足條件。如果 T 是可調(diào)用對(duì)象(例如 lambda 或函數(shù)對(duì)象),則調(diào)用它并返回結(jié)果。
    • 如果 T 不是可調(diào)用對(duì)象,則將其轉(zhuǎn)換為 bool
  • 類型萃取std::is_invocable_v
    • 這是一個(gè)用于判斷類型 T 是否可調(diào)用的特性。如果 T 是可調(diào)用對(duì)象,則 std::is_invocable_v<T> 返回 true。
    • 需要包含 <type_traits> 頭文件
  • 完美轉(zhuǎn)發(fā) std::forward
    • std::forward<T>(value) 確保參數(shù)的完美轉(zhuǎn)發(fā),保留其左值或右值性質(zhì)。
  • 可變長(zhǎng)參數(shù)模板:支持可變數(shù)量的參數(shù)包,語(yǔ)法用 T ... args表示。
  • 折疊表達(dá)式
    • 使用了C++17中的折疊表達(dá)式 ,它會(huì)對(duì)參數(shù)從左到右進(jìn)行求值。
    • 簡(jiǎn)化了可變長(zhǎng)參數(shù)模板的使用,提供了一種簡(jiǎn)潔而直觀的方式來(lái)對(duì)參數(shù)包進(jìn)行展開(kāi)和操作,從而避免了遞歸或顯式循環(huán)的繁瑣。
  • 結(jié)構(gòu)化綁定 std::bind :可綁定參數(shù)args到一個(gè)函數(shù)f,并返回一個(gè)可調(diào)用對(duì)象。

參考

到此這篇關(guān)于C++17 使用折疊表達(dá)式實(shí)現(xiàn)一個(gè)IsAllTrue函數(shù)的文章就介紹到這了,更多相關(guān)C++ IsAllTrue函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Windows下Qt打包自動(dòng)尋找依賴的DLL

    Windows下Qt打包自動(dòng)尋找依賴的DLL

    本文介紹了兩種在Windows下使用Qt打包應(yīng)用程序并自動(dòng)尋找依賴DLL的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • C++11標(biāo)準(zhǔn)庫(kù) 互斥鎖 <mutex> 詳解

    C++11標(biāo)準(zhǔn)庫(kù) 互斥鎖 <mutex> 詳解

    這篇文章主要介紹了C++11標(biāo)準(zhǔn)庫(kù)互斥鎖 <mutex> 的相關(guān)知識(shí),使用call_once()的時(shí)候,需要一個(gè)once_flag作為call_once()的傳入?yún)?shù),本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2024-07-07
  • C語(yǔ)言實(shí)現(xiàn)合并字符串

    C語(yǔ)言實(shí)現(xiàn)合并字符串

    今天小編就為大家分享一篇C語(yǔ)言實(shí)現(xiàn)合并字符串,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-12-12
  • C++實(shí)現(xiàn)Dijkstra算法的示例代碼

    C++實(shí)現(xiàn)Dijkstra算法的示例代碼

    迪杰斯特拉算法(Dijkstra)是由荷蘭計(jì)算機(jī)科學(xué)家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是從一個(gè)頂點(diǎn)到其余各頂點(diǎn)的最短路徑算法。本文將用C++實(shí)現(xiàn)Dijkstra算法,需要的可以參考一下
    2022-07-07
  • C語(yǔ)言實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)和雙向鏈表操作

    C語(yǔ)言實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)和雙向鏈表操作

    這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)雙向鏈表操作,需要的朋友可以參考下
    2017-03-03
  • C++超詳細(xì)講解模板的使用

    C++超詳細(xì)講解模板的使用

    這篇文章主要介紹了C++中模板(Template)的詳解及其作用介紹,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易五子棋小游戲

    C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易五子棋小游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單五子棋小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • linux C 打印錯(cuò)誤信息和標(biāo)準(zhǔn)輸入輸出詳細(xì)介紹

    linux C 打印錯(cuò)誤信息和標(biāo)準(zhǔn)輸入輸出詳細(xì)介紹

    這篇文章主要介紹了linux C 打印錯(cuò)誤信息和標(biāo)準(zhǔn)輸入輸出詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下
    2016-12-12
  • 用C語(yǔ)言簡(jiǎn)單實(shí)現(xiàn)掃雷小游戲

    用C語(yǔ)言簡(jiǎn)單實(shí)現(xiàn)掃雷小游戲

    這篇文章主要為大家詳細(xì)介紹了用C語(yǔ)言簡(jiǎn)單實(shí)現(xiàn)掃雷小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 淺談C++11的std::function源碼解析

    淺談C++11的std::function源碼解析

    類模版std::function是一種通用的多態(tài)函數(shù)包裝器std::function的實(shí)例可以對(duì)任何可以調(diào)用的目標(biāo)實(shí)體進(jìn)行存儲(chǔ)、復(fù)制、和調(diào)用操作,本文詳細(xì)的介紹一下,感興趣的可以了解一下
    2021-06-06

最新評(píng)論