茂加部珈琲店

主にtech関連のメモ置き場です

可変長テンプレートでunion

C++勉強ネタが続きます

templateを作って直和型もどきを作りたいときがあります。
これはすでにBoost Libraryで実現されていて、boost::variantとして利用できます。
また、C++1zでは、これが標準に追加されます。

今回は、可変長テンプレートを使ってこれを実装してみたいと思い立ち書いてます。 boost::variantはインデックス用の数字を保存しますが、今回は値だけを格納することにします。

とりあえず、データ構造はこんな感じでしょうか
U[3]U.nxt.nxt.nxt.vになるみたいなイメージです

template <size_t N, class Head, class ...Tail>
union U {
  Head v;
  U<N-1 ,Tail...> nxt;
};

template <class Head,class ...Tail>
union U<0,Head,Tail...> {
  Head v;
};

これを初期化するクラスを作っておきます

template <class ...Args>
class Union{
  U<sizeof...(Args)-1,Args...> u;
};

中身を取り出したいので、

auto v3 = get<3>(u); 

みたいにかけるようにしたいですね

template <size_t N>
struct _getter {
  template <size_t U_N, class U_Head, class... U_Tail>
  static auto get(const U<U_N,U_Head,U_Tail...>& u){
    return _getter<N-1>::get(u.nxt);
  }
};

template <>
struct _getter<0>{
  template <size_t U_N, class U_Head, class... U_Tail>
  static auto get(const U<U_N,U_Head,U_Tail...>& u){
    return u.v;
  }
};

template <size_t N, class...Args>
auto get(const Union<Args...>& u){
  return _getter<N>::get(u.u);
}

うーん…

int main(){
  union _u {
    char c;
    double d;
    int i;
  };  
  Union<char ,double ,int> uni;
  variant<char, double ,int> var;
  auto v0 = get<0>(uni); 
  
  cout << sizeof(v0) << endl;   // 1
  cout << sizeof(_u) << endl;   // 8
  cout << sizeof(uni) << endl;  // 8
  cout << sizeof(var) << endl;  // 16
}

unionと同サイズになることがわかりますね。
独自の型を実装していて、セレクタが不要な場合などに使えそう。