• <menu id="sssag"></menu>
  • <menu id="sssag"></menu>
  • C++霧中風景18:C++20, 從concept開始

    轉眼間,C++20的標準已經發布快兩年了。不少C++的開源項目也已經將標準升級到最新的C++20了,筆者也開啟了新標準的學習歷程了。所以借這系列的博文,記錄下筆者學習新標準的一些心得與吐槽~~
    作為C++20系列的第一篇開篇之文,就要從千呼萬喚始處理的concept聊起了,后續很多新的feature的實現,也仰賴新的concept的實現,后續筆者的文章也會逐步展開。OK,開始我們C++20旅程的第一站:concept

    1.First Look

    先從一個群友的一個實際的問題出發,我們來看看concept可以解決什么問題。是怎么樣通過coding實現的。
    群里的一個問題

    • SFINAE
      熟悉C++模板編程的小伙伴肯定第一時間想到通過SFINAE的方式來解決,讓筆者來解決這個問題的話,會寫出下面的代碼:
    template <typename T>
    T test(T a) {
        static_assert(!std::is_same_v<void, decltype(a + a)>);
        static_assert(!std::is_same_v<void, decltype(a - a)>);
        static_assert(!std::is_same_v<void, decltype(a * a)>);
        static_assert(!std::is_same_v<void, decltype(a / a)>);
        return a;
    }
    

    這里寫的代碼是一個略微Trick的表達,利用decltype去獲取操作符計算后的類型,然后用std::is_same_v進行一個其實沒什么意義的類型比較,來滿足static_assert的語義,最終滿足我們對模板類型T的一些約束。

    • concept
      顯然上面的方式是很不直觀的,雖然能達到咱們的目的,但是從代碼優雅角度來說是一種較差的選擇實現。

    我們來看一下用C++20提供給我們的Concept是如何解決這個問題的:

    template <typename T>
    concept Cal = requires (T a) {
        a + a;
        a - a;
        a * a;
        a / a;
    };
    
    template <typename T>
    requires Cal<T>
    T test(T a) {
        return a;
    }
    

    這是通過concept來實現的一個類型約束方式,Cal代表著一個concept的實現,requires中花括號的內容就代表了對于類型T的約束,要滿足下面的操作符

    a + a;
    a - a;
    a * a;
    a / a;
    

    Bingo! 似乎C++20給了我們一個更好的trait,接著往下看,我們繼續來細探Concept的實現。

    2. How to use

    • concept的定義

    這里寫了一個例子,咱們基于這個例子來展開:

    template <typename T>
    concept Cal = requires (T a) {
        a + a;
        typename T::type;
       {a + a} -> std::same_as<float>;
        require  Concept2<T>;
    };
    

    這里定義了4個requires的要求,只有滿足這4個條件才能通過concept的限制,正常進行編譯。

    1). a + a這個是最簡單的,該表達式能通過編譯則代表符合要求,這里不會進行實際的計算。
    2). typename T::type代表需要,T類型定義了type類型,才符合要求
    3). {a + a} -> std::same_as<float> 這里的{}代表了decltype(a + a)之后的類型需要滿足后面的concept的需求。這是一個語法糖,也可以通過這樣來實現:requires std::is_floating_point_v<decltype(a + a)>
    4). 最后的require Concept2<T>這代表了concept的嵌套邏輯。requires后面可以帶任意的concept

    • concept的使用

    了解了concept定義之后,我們就可以利用concept來進行模板類型的約束了。
    這里“回字有四種寫法”,大家可以選擇自己喜歡的方式來使用。(真搞不懂搞這么多寫法干什么,不能統一一下嗎?, 下面介紹的順序隨筆者的偏好以此遞減)

    template<typename T> 
    requires Cal<T>
    T test(T a)
    {
        return a + a;
    }
    

    1). 這是筆者最認可的一種書寫方式,語義明確,在模板類型定義之后明確對它的要求。

    template<Cal T> 
    T test(T a)
    {
        return a + a;
    }
    

    2). 也還行吧,模板參數前指定了concept,也比較明確直觀。

    Cal test(Cal a)
    {
        return a + a;
    }
    

    3). concept命名一長就顯得有些瑣碎了,并且把concept當類型使用了,看起來有些怪異了。

    template<typename T> 
    T test(T a) requires Cal<T>
    {
        return a + a;
    }
    

    4). 把concept寫后面的都是異端,當然如果你喜歡的話,也ok。

    3. concept的本質

    concept本質上是:一個可以套用在模板上的constexpr bool值。

    相信下面這段代碼能幫助你更好的理解它:

    template <typename T>
    concept Con = std::is_floating_point_v<T>;
    
    int main(int argc, char* argv[]) {
        static_assert(std::is_same_v<decltype(Con<float>), bool>);
        std::cout << Con<int> << std::endl;
        return 0;
    }
    

    顯然,上面的代碼我們用is_same_v確定了concept生成的類型就是bool類型。而同樣的,在運行期,咱們也可以將concept的結果作為一個bool常量進行使用,并打印。

    所以,take it easy。 concept很簡單,它只是C++20給你提供的一個better的工具,來擺脫被瘋狂的模板報錯所支配的恐懼。但即使你完全不了解它,使用老的方式,依然能夠同樣解決問題。

    4.小結

    C++的一些模板推斷的錯誤常常讓人抓狂。而很多時候我們使用它需要

    • 要進行模板推斷類型的編程設計
    • 利用SFINAE的方式來類型約束

    這無形之中增加Coding時的心智成本,而concept作為一個新的語法糖,給了我們拆分二者的機會:讓上帝歸上帝,凱撒歸凱撒。
    使用好concept來進行類型約束,enjoy新標準帶來的便利吧。

    希望大家能夠有所收獲,筆者水平有限。成文之處難免有理解謬誤之處,歡迎大家多多討論,指教。

    5.參考資料

    CppReference

    posted @ 2022-03-12 20:38  HappenLee  閱讀(35)  評論(0編輯  收藏  舉報
    国产在线码观看超清无码视频,人妻精品动漫H无码,十大看黄台高清视频,国产在线无码视频一区二区三区,国产男女乱婬真视频免费,免费看女人的隐私超爽,狠狠色狠狠色综合久久蜜芽