新しいキーワードの作り方

Boostのtribool をみていたらおもしろいコードを発見。理解するまでちょっと時間が掛かってしまいました。

以下におもしろい部分だけを抜粋。

namespace detail {
    struct indeterminate_t
    {
    };
}

typedef bool (*indeterminate_keyword_t)(tribool, detail::indeterminate_t);

inline bool
indeterminate(tribool x,
              detail::indeterminate_t dummy = detail::indeterminate_t());

class tribool
{
public:
    enum value_t
    {
        false_value,
        true_value,
        indeterminate_value
    } m_value;

    Tribool() : m_value( false_value ) {}
    Tribool(bool value) : m_value(value? true_value : false_value) {}
    Tribool(indeterminate_keyword_t) : m_value(indeterminate_value) {}
};

3値論理型の リテラルキーワードとして、

  1. true
  2. false
  3. indeterminate

をとれるようにします。つまり、

tribool t = true;
tribool f = false;
tribool i = indeterminate;

のようなことが出来るようにします。で、この キーワード(に見える) indeterminate は実は

bool indeterminate( tribool, inditerminate_t )

型の関数ポインタです。 で、引数に tribool と inditermnate_t をとり、boolを返す関数のポインタ型を indeterminate_keyword_t 型として定義しています。

この indeterminate_keyword_t 型で 論理演算まわりのオペレータのオーバーロードを定義しまくって 自然に tribool を使えるようにしているのです。 (なお、ローカルな名前空間上の構造体 indeterminate_t 型 を引数に含めることで たまたま同じ 関数ポインタ型になってしまうものが生まれるのをブロックしています)

欲張りでちょっと読みにくくしていいる tribool

でもだったら、関数 indeterminate はbool を返したり tribool を引数にとったりする 必要は無いはず。この訳のわからない引数が可読性を落としています。

しかしこれは、三値論理値が不定か?(isInditerminate) を問い合わせる関数の名前も inditerminate にしたいという 名前の多重仕様を行っているからです。

inline bool indeterminate(tribool x, detail::indeterminate_t)
{
  return x.value == tribool::indeterminate_value;
}

こう使います。(二つめの引数は宣言にて 捨て駒のデフォルト引数が定義されているので、使うときは tribool を一つ取る関数として使います。)

tribool t = true;
tribool f = false;
tribool i = indeterminate;

indeterminate( t );  // false が返る
indeterminate( f );  // false が返る
indeterminate( i );  // true が返る

便利でかっこいいけどちょっとトリッキーですね。トリックの方はともかく、キーワード風なものを 関数ポインタ型で作り出せてしまうのは 使い道が広そうです。

いろいろ参考になりそうな実装です。