黃爸爸狗園

本園只有sanitizer,沒有狗籠

0%

C++在編譯期做出多重型別物件

詳細的解說如何在編譯期做出一個具有多重型別的物件

神偷小吉從在S社工作的朋友K那裡幹來的程式碼

  • Code在此,本文要來一步一步拆解這段code在幹嘛

    1
    2
    3
    4
    5
    6
    7
    8
    9
    let i = 11;
    let b = false;
    let s = std::string{"oh yes"};

    let v = Multi{
    [&] { return i; },
    [&] { return b; },
    [&] { return s; },
    };

  • 首先我們可以看到本文主角Multi,一個神祕物件,初始化的時候吃了一個塞了三個不同return type的lambda的initialize list

    • let的定義是#define let const auto
    • 這個Multi是何方神聖?
1
2
3
4
5
6
7
8
9
10
11
12
13
template <typename T>
struct To {
template <typename F>
To(F f) : f(std::move(f)) {}

operator T() const { return f(); };

private:
const std::function<T()> f;
};

template <typename... Ts>
struct Multi : public To<Ts>... {};
  • Multi是一個struct,繼承了To
    • 等等,這個如同財哥專業檳榔的繼承是怎麼回事?
    • Keyword: variadic template
      • 我們來看個範例
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        struct A {

        };

        struct B {

        };

        struct C {

        };

        template <typename... Ts>
        struct Multi : public Ts... {

        };

        int main() {
        Multi<A, B, C> m ;
        return 0 ;
        }
      • 基本上Ts...表示你可以給很多個type,他會幫你自動補上",",like this
        1
        2
        3
        4
        template<>
        struct Multi<A, B, C> : public A, public B, public C
        {
        };
    • 所以struct Multi : public To<Ts>...被轉換成了
      • struct Multi : public To<Ts#1>, To<Ts#2>, ......
      • 在這個例子中,Ts#n這些東西會變成什麼?
      • struct Multi: public To<lambda#A>, To<lambda#B>嗎?
        • 仔細看看To內部
          1
          2
          3
          4
          5
          template <typename T> 
          struct To {
          // ...
          operator T() const { return f(); };
          // ...
        • T是一個return type,看起來絕對不會是lambda
      • 中間似乎差了一個步驟......?
  • User-defined deduction guides
    • 我們仔細看一下有一段code
      1
      2
      template <typename... Ts>
      Multi(Ts...) -> Multi<std::invoke_result_t<Ts>...>;
      • 這到底是什麼東西? function prototype?
        • 非也! 這個可是C++17的新特性Class template argument deduction (CTAD)
        • 其中的一個概念:User-defined deduction guides
          • 人為的去教compiler如何實例化你的type
      • 所以上面那段code是什麼意思?
        • 這段code的意思是如果今天出現了一個type Multi(Ts#1, Ts#2, ......),我們會引導compiler把它變成
          1
          2
          3
          4
          5
          Multi<
          std::invoke_result_t<Ts#1>,
          std::invoke_result_t<Ts#2>,
          ...
          >
      • invoke_result_t會在compile tile推導callable的return type
    • 統整一下上面所述,整個轉換的流程為(對應我們範例程式碼的行數)
    1. const auto v = Multi { lambda, lambda, lambda } ; [30.]
    2. Multi(lambda, lambda, lambda) -> Multi<std::invoke_result_t, std::invoke_result_t, std::invoke_result_t> [23.]
    3. Multi(lambda, lambda, lambda) -> Multi<int, bool, std::string>[23.]
    4. Multi<int, bool, std::string> -> struct Multi : public To<int>, To<bool>, To<std::string>[20.]
  • 接下來情況就很明朗了,我們得到了一個有多重繼承的物件,最後就是看static_cast<>轉成誰,就會變成誰,Magic!

延伸閱讀

小結

  • 我說完了,我說 完了