[C++] 暇だし共用体をラップしたクラス作った

 いや暇じゃないよ。精一杯の現実逃避だよ。共用体ってそこそこ便利なんだけど、いちいち定義して使うの面倒じゃん?そんなわけでテンプレートクラスにしてみました。

 本当はあまり役に立ちそうにないコードが書きたかっただけです。

コード

 コード貼っつけます。共用体をラップしただけなので、PODしか持てないです。その辺の制約は共用体と同じです。それ以上のことをやりたい場合はboost::variantとかを使いましょう。

 なんかこう、もっと綺麗にかけないものなのだろうか…

適当な解説

 デフォルトのテンプレート引数を設定して、可変長引数っぽく見せます。上のサンプルでは8個で定義していますがもっと増やせます。マクロで書かれている部分を増やして数字も1ずつ増やしてやってください。基本はそんな感じです。

 _voidという名前で定義されているのが、型が指定されていなかった場合に指定される型です。中身はありません。型が指定された分だけunionで内部に値を保持し、_getや_setでそれぞれの方を取得します。保持する型の指定が少ない場合は_void型の_getや_setが複数存在しコンパイルエラーになってしまうので、この点だけ少し工夫が必要になります。

 おなじみのSFINAEですが、さらっと解説を加えておくと、テンプレート関数の実体化で存在しない型にアクセスした場合にエラーではなくオーバーロード対象から外す、という挙動のことです。これによってうまい具合にテンプレート関数のオーバーロードができたりします。しかし、SFINAEが働く以前に定義が重複している場合は、最終的にSFINAEでオーバーロード対象から外されるとしても、多重定義としてエラーになります。ここがちょっとむずかしいところ。つまり、Unionクラスへの型の指定が少ない場合は、_void型を引数に取る_getや_setが複数生成されてしまい、コンパイルが通りません。

template<bool V, typename T>
struct enable { };

template<typename T>
struct enable<true, T>
{
    typedef T type;
}

template<typename T>
void hoge(const int x, enable<true, T>::type* = 0) { }

// SFINAEで弾かれるけど、xが重複してるのでダメ!
template<typename T>
void hoge(const int x, enable<false, T>::type* = 0) { }

 そこで、上記のコードではguardというテンプレートクラスを導入しています。

template<typename T, int N>
struct guard
{
    typedef T type;
};

template<int N>
struct guard<_void, N>
{
    typedef guard<_void, N - 1> type;
};

 この部分です。2つ引数をとるテンプレートクラスで、Tが_voidでないときtypeをTの別名とし、Tが_voidのときguard<_void, N>をtypeとします。これにより、Nの値が異なると_voidごとに別々のクラスがtypeに割り当てられます。これで_voidがいくつ指定されても重複しない型を作ることが出来ました。Tが_voidでない時はそのままの型になるので、オーバーロードに使えます。_getで使用しないにもかかわらずTを引数として受け取っているのは、guard::typeによってオーバーロードを解決するためです。

 あとはインターフェースになる代入演算子やら、中に入っている値を確認するためのwhichを追加しておしまいです。なんかこんなことしなくてももっと簡単にできるようなきがするんだけどなー。まぁラップしただけだし共用体定義して使えって話だよなー。

0 件のコメント :

コメントを投稿