<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      C++ 使用分治減小模板遞歸深度

      起因

      C++14 引入 STL 的 make_index_sequence 可以生成一個類型為 std::size_t,0 到 N-1 的編譯期序列,我們可以這樣使用它:

      代碼
      
      //利用函數參數推導提取序列
      template<std::size_t... Seq>
      void foo(std::index_sequence<Seq...>)
      {
          //使用Seq
      }
      //利用模板特化提取序列 template<typename> struct extract_sequence; template<std::size_t... Seq> struct extract_sequence<std::index_sequence<Seq...>> { //使用Seq };
      foo(std::make_index_sequence<5>{});//foo內部得到 {0, 1, 2, 3, 4} extract_sequence<std::make_index_sequence<3>>;//extract_sequence內部得到 {0, 1, 2}

      可是在我的程序中,我想創建一個等差數列,std::make_index_sequence 無法直接拿來使用(其實可以間接使用,后文會提到)。于是我編寫了一個自己的版本:

      代碼
      
      //主模板,遞歸實例化
      template<std::size_t Base,std::size_t Step, std::size_t Count, std::size_t... Seq>
      struct make_arithmetic_progression : make_arithmetic_progression<Base + Step, Step, Count-1, Seq..., Base>
      {
      };
      //遞歸終點特化版本 template<std::size_t Base, std::size_t Step, std::size_t... Seq> struct make_arithmetic_progression<Base, Step, 0, Seq...> { using type = std::index_sequence<Seq...>; };
      template<std::size_t Base, std::size_t Step, std::size_t Count> using arithmetic_progression = typename make_arithmetic_progression<Base, Step, Count>::type;
      foo(arithmetic_progression<1, 2, 3>{});//foo內部得到 {1, 3, 5} extract_sequence<arithmetic_progression<3, 3, 3>>;//extract_sequence內部得到 {3, 6, 9}

      問題

      這個實現看起來沒什么問題,可以正確地生成任意指定的等差數列。但是在一個偶然的情況下,我在創建一個很長的數列時,遇到以下錯誤:

      recursive overflow

      模板遞歸實例化深度超過限制了,查閱了一下資料,獲知各家編譯器都對這個深度有一個默認限制,GCC 和 clang 都是 1024,MSVC 是 499。

      可以使用編譯參數來指定這個限制:

      代碼
      
      -ftemplate-depth=N //GCC 和 clang
      /Zc:templateDepth=N //MSVC
      

      但這顯然是下策,每次編譯這個模板都需要在編譯時加上額外的指令,非常麻煩。

      解決

      自定義方式

      我們知道有個排序算法叫歸并排序,將長列表不斷拆分成最小單元后合并。我們的這個數列實現也可以直接借鑒這個思路,拆分數組后合并:

      代碼
      
      //list拼接類主模板
      template<typename, typename>
      struct index_list_concat;
      //list拼接類特化版本 template<std::size_t... Seq1, std::size_t... Seq2> struct index_list_concat<std::index_sequence<Seq1...>, std::index_sequence<Seq2...>> { using type = std::index_sequence<Seq1..., Seq2...>; };
      //輔助分割主模板 template<std::size_t Base, std::size_t Step, std::size_t Count> class make_arithmetic_progression { static constexpr std::size_t HalfCount = Count / 2; static constexpr std::size_t HalfBase = Base + HalfCount * Step;
      public: using type = typename index_list_concat<typename make_arithmetic_progression<Base, Step, HalfCount>::type, typename make_arithmetic_progression<HalfBase, Step, Count - HalfCount>::type>::type; };
      //拆分為1個元素特化版本 template<std::size_t Base, std::size_t Step> class make_arithmetic_progression<Base, Step, 1> { public: using type = index_list<Base>; };
      //拆分為2個元素特化版本 template<std::size_t Base, std::size_t Step> class make_arithmetic_progression<Base, Step, 2> { public: using type = index_list<Base, Base + Step>; };
      template<std::size_t Base, std::size_t Step, std::size_t Count> using arithmetic_progression = typename make_arithmetic_progression<Base, Step, Count>::type;

      這樣對于長度為 Count 的序列,模板遞歸實例化的深度變為 $log_2Count$,按照 MSVC 的默認最大深度 499 來算,也能支持到 $2^{499}$ 長度的序列,這無論如何都夠用了,更別說 GCC 和 clang 的 1024 了。

      借用標準庫

      上文說到可以間接使用標準庫來實現這個等差數列模板。在我們初中的時候就知道,可以用公差為 1 的等差數列生成任意的其他等差數列:

      $$ \begin{aligned} a_n&=n (n=0,1,2,...)
      \\\\ b_n&=a_n*d+k (n=0,1,2,...)//d為公差,k為首項 \end{aligned} $$ 結合 C++17 的折疊表達式,我們可以構建這樣的模板:
      代碼
      
      //輔助推導函數
      template<std::size_t Base, std::size_t Step, std::size_t... Seq>
      std::index_sequence<(Base + Step * Seq)...> arithmetic_progression_helper(std::index_sequence<Seq...>);
      template<std::size_t Base, std::size_t Step, std::size_t Count> using arithmetic_progression = decltype(arithmetic_progression_helper<Base, Step>(std::make_index_sequence<Count>{}));

      這個模板只是在 STL 的 index_sequence 上做了一次包裝,更加簡潔。

      優劣

      在我對上述兩種實現進行測試時,發現在實例化超長的數列時,使用 STL 的版本無論在速度上還是內存消耗上都顯著優于自定義版本。

      比如在我的電腦上使用 clang 編譯,實例化一個長度為 100000 的數列。STL 版本的實現編譯耗時 1 秒左右,內存僅占用數十 MB;而自定義版本的編譯卻需要 10 秒左右,內存占用超過 1.2GB。

      為什么相差如此懸殊?我于是很好奇 STL 是如何實現 make_index_sequence 的,找到源碼后跟進去一瞧,它其實是 __make_integer_seq<std::size_t, N>。大家都知道,前導兩個下劃線的 STL 模板是內部實現版本,它們要么由編譯器支持沒有源碼,要么有源碼但不建議直接使用,這個 __make_integer_seq 屬于前者。這些由編譯器支持的模板大部分屬于用戶端不可能實現或者實現極為復雜,而在編譯器端實現會相對簡單并且高效的。

      這提醒我們,對于標準庫能夠直接或間接支持的模塊,應該優先選擇使用標準庫來實現,它們會幫我們最大化地利用資源。不過在學習過程中,編寫自己的版本也是很有價值的,能夠讓我們深入細節,接觸優秀的編碼思想和優化策略。

      總結

      使用分治來降低模板遞歸實例化深度這一策略,是我以前從沒考慮過的,雖然它在本次編碼中被最終舍棄,但是日后說不定有用武之地。本篇學習記錄梳理了我在逐步實現和改進等差數列模板的思路,總結如下:

      1. 學習過程中不斷造輪子加驗證是獲取和掌握知識的有效途徑;

      2. 在實際項目中,盡量使用經過大眾檢驗的庫有助于項目的穩定高效。

      對于第二條,如果為了一個模塊而引入一整個大型庫的依賴,仍須仔細斟酌。這與本篇筆記的內容無關,但在實際工作中,如果每個人都為了實現一個簡單模塊而肆無忌憚的引入三方依賴,造成團隊項目倉庫中一大半是三方庫內容的情況還是很讓人不舒服的。

      posted @ 2025-08-24 17:36  saltymilk  閱讀(207)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 日本人一区二区在线观看| 精品久久久久久无码人妻蜜桃 | 久久国产免费观看精品3| 鹤山市| 日韩精品毛片一区到三区| 国产粉嫩一区二区三区av| 女人被爽到高潮视频免费国产| 国产女人喷潮视频免费| 亚洲a片无码一区二区蜜桃| 开心五月激情综合久久爱| 国产精品综合色区av| 成年午夜免费韩国做受视频| 日韩一区二区三区不卡片| 亚洲精品天堂在线观看| 国内精品免费久久久久电影院97| 亚洲最大成人av在线天堂网| 377p日本欧洲亚洲大胆张筱雨| 亚洲精品无码成人A片九色播放| 在线无码免费看黄网站| 国产AV无码专区亚洲AV紧身裤 | 亚洲成色精品一二三区| 高潮喷水抽搐无码免费| yy111111在线尤物| 国产精品久久久国产盗摄| 九九久久人妻精品一区色| 亚洲AV无码国产永久播放蜜芽| av一区二区中文字幕| 国产精品一区二区麻豆蜜桃| 武装少女在线观看高清完整版免费| 综合图区亚洲另类偷窥| 内地偷拍一区二区三区| 国产一区二区三区内射高清| 国产精品无码一区二区牛牛| 中文字幕av无码不卡| 亚洲国产精品自产在线播放| 青青草国产精品一区二区| 亚洲熟妇精品一区二区| 花莲县| 一区二区三区四区亚洲自拍| 国产亚洲综合欧美视频| 国产情侣激情在线对白|