traitsとtag dispatchがわかった

やっとわかった。
これは、ライブラリを使うだけでなくて書いてみないとなかなかわからないな。
以下 p[0]とp.xというインタフェースの違うものを一緒くたに扱うための努力。

#include <iostream>

namespace di {
  // 関数分岐用のタグ
  struct unknown_tag {};
  struct use_access_operator_tag {};

  // 何もしない非特殊化テンプレート
  template<typename T>
  struct elements_traits
  {
    typedef unknown_tag tag;
    typedef void value;
  };

  // ポインタ用に特殊化
  template<typename T>
  struct elements_traits<T*>
  {
    typedef use_access_operator_tag tag;
    typedef T& value;
  };

  namespace detail {
    // use_access_operator_tag用
    // operator[]で要素にアクセスする
    template<typename T>
    typename elements_traits<T>::value
    x(T &t, use_access_operator_tag)
    {
      return t[0];
    }

    template<typename T>
    typename elements_traits<T>::value
    y(T &t, use_access_operator_tag)
    {
      return t[1];
    }
  }

  // 第1要素取得
  template<typename T>
  typename elements_traits<T>::value
  x(T t)
  {
    // T型のtraitsを実体化してオーバーロードする
    typename elements_traits<T>::tag tag;
    return detail::x(t, tag);
  }

  // 第2要素取得
  template<typename T>
  typename elements_traits<T>::value
  y(T t)
  {
    // T型のtraitsを実体化してオーバーロードする
    typename elements_traits<T>::tag tag;
    return detail::y(t, tag);
  }

} // namespace di

以下使うとこ。

template<typename T>
void func(T t)
{
  std::cout << di::x(t) << ',' << di::y(t) << std::endl;
}

int main(int argc, char **argv)
{
  int a[]={11, 22};
  func(a);

  return 0;
}

上記まででとりあえずT型のポインタを処理できる(0, 1にアクセスしても大丈夫という前提で)。
さらにfuncに対してこんなのを使いたくなったとする。

// PointXY
struct PointXY
{
  int x;
  int y;
};

operator[]を定義してインタフェースをポインタとあわせてしまうというのが普通?の
やり方だが今回はこのまま使う。
まず、追加でxyに対応するtagとアクセサを追加する。

namespace di {
  // 分岐用のタグ定義
  struct use_xy_member_tag {};

  namespace detail {
    // use_xy_member_tag用
    // x, y要素にアクセスする    
    template<typename T>
    typename elements_traits<T>::value
    x(T &t, use_xy_member_tag)
    {
      return t.x;
    }

    template<typename T>
    typename elements_traits<T>::value
    y(T &t, use_xy_member_tag)
    {
      return t.y;
    }
  }
}

次に、PointXYクラスをuse_xy_member_tagと結び付けて中身の型がわかるようにtraitsを追記する。

// 特殊化
template<>
struct di::elements_traits<PointXY>
{
  typedef use_xy_member_tag tag;
  typedef int& value;
};

これで準備完了。
あとは

PointXY b={33, 44};
func(b);

で動きます。

ついでに

// PointOverload
class PointOverload
{
  int value[2];
public:
  PointOverload(const int x, const int y)
  {
    value[0]=x;
    value[1]=y;
  }

  int&
  operator[](const int index){ return value[index]; }
};

template<>
struct di::elements_traits<PointOverload>
{
  typedef use_access_operator_tag tag;
  typedef int& value;
};
PointOverload c(55, 66);
func(c):