メンバーアクセス演算子で最下層オブジェクトまでの透過的なアクセスを提供する

C++におけるクラスのメンバーアクセス演算子（operator ->）は、特徴的な挙動を取る。それは、戻り値の型がポインタでなければ、その戻り値に対して更に暗黙的にメンバーアクセス演算子を呼ぶという特徴である。具体的な挙動を以下のコードで説明する。

```cpp
#include <iostream>

struct A {
  A* operator ->() { std::cout << typeid(this).name(); return this; }
  int value;
};

struct B {
  A& operator ->() { std::cout << typeid(this).name(); return a; }
  A a;
};

struct C {
  B& operator ->() { std::cout << typeid(this).name(); return b; }
  B b;
};

struct D {
  C& operator ->() { std::cout << typeid(this).name(); return c; }
  C c;
};

int main() {
  A a;
  a->value = 1;
  std::cout << a.value << std::endl;
  B b;
  b->value = 2;
  std::cout << b.a.value << std::endl;
  C c;
  c->value = 3;
  std::cout << c.b.a.value << std::endl;
  D d;
  d->value = 4;
  std::cout << d.c.b.a.value << std::endl;
}
```

このプログラムを実行すると以下の出力を得られる。

```
P1A1
P1BP1A2
P1CP1BP1A3
P1DP1CP1BP1A4
```

ここで、A、B、C、Dのクラスは、それぞれメンバーアクセス演算子をオーバーロードする。DはCの参照を返し、CはBの参照を返し、BはAの参照を返し、Aは自身のポインタを返す。すると、各クラスのオブジェクトのメンバーアクセス演算子からAのメンバ変数であるvalueへ透過的なアクセスができるのである。また、透過的といっても、各クラスのメンバーアクセス演算子を暗黙的に呼び出している事が分かる。

さて、この特徴は任意のオブジェクトを保持する様なラッパークラスを利用する際に、ラッパークラスを経由するコードを書く必要がなくなるという利点がある。例えば、テンプレートによって任意のオブジェクトを保持するラッパークラスに、各クラスを保持する場合を考える。

```cpp
template <class T>
struct Holder {
  T& operator ->() { return value_; }
  T value_;
};

int main() {
  Holder<Holder<Holder<D>>> h {};
  h->value = 42;
  std::cout << h.value_.value_.value_.c.b.a.value << std::endl; // 42
}
```

ここで、Holderクラスは任意のオブジェクトを保持するだけのラッパークラスである。参照を返すメンバーアクセス演算子によって、3段階のラッピングであるにも係わらず、透過的にオブジェクトにアクセスする事ができる。しかし、保持するオブジェクトがポインタを返すメンバーアクセス演算子を持っている場合、そのクラスで演算子の呼び出しが中断される。例えば、Cのメンバーアクセス演算子で、Bの参照ではなくBのポインタを返すように変更すると、演算子の呼び出しはBで完了する。

```cpp
struct A {
  A* operator ->() { std::cout << typeid(this).name(); return this; }
  int value;
};

struct B {
  A& operator ->() { std::cout << typeid(this).name(); return a; }
  A a;
};

struct C {
  B* operator ->() { std::cout << typeid(this).name(); return &b; }
  B b;
};

struct D {
  C& operator ->() { std::cout << typeid(this).name(); return c; }
  C c;
};

template <class T>
struct Holder {
  T& operator ->() { return value_; }
  T value_;
};

int main() {
  Holder<D> h {};
  h->value = 42; // error
}
```

殆どの場合、この様な挙動は製作者の意図通りである為、問題にはならない。しかし折角Bではメンバーアクセス演算子がオーバーロードされているのであるから、これを暗黙的に呼び出すのが**粋な計らい**、若しくは、~~余計なお世話~~というものではないだろうか。
そこで、ラッパークラスであるHolderが保持するオブジェクトに対して、最下層オブジェクトまでの透過的なアクセスを提供する機能を追加する。

ここで、最下層オブジェクトというのは、メンバーアクセス演算子を持たないか、メンバーアクセス演算子で自身のポインタを返すオブジェクトである。生のポインタはメンバーアクセス演算子が有効であるため、最下層オブジェクトではない。任意のオブジェクトについて、メンバーアクセスが可能かどうかを確認するにはC++のSFINAEを利用する。以下のコードでは、任意の型Tについて、operator ->を呼び出し、その戻り値の元々の型がTとは異なる場合のhas_arrowと、それ以外の、可変長引数を受け取るhas_arrowを用意する。has_arrowに適当なポインタを渡した時に、Tのoperator->が有効であればstd::true_typeを返し、そうでなければstd::false_typeを返す。

```cpp
template <class T, class U = decltype(std::declval<T>().operator ->())>
std::true_type has_arrow(std::enable_if_t<!std::is_same<std::decay_t<T>, std::decay_t<std::remove_pointer_t<U>>>::value, std::nullptr_t>);

template <class T>
std::false_type has_arrow(...);
```

次に、has_arrowを利用して最下層オブジェクトに到達するまでメンバーアクセスを繰り返す機構を作成する。以下のコードでは、arrow_helper関数に任意のオブジェクトを渡すと、そのオブジェクトのメンバーアクセス演算子が有効であれば、演算子の戻り値に対して再帰的にarrow_helperを呼び出している。なお、メンバーアクセス演算子によってポインタが返された場合、そのポインタの元々の型について再帰的に演算子を呼び出している。

```cpp
template <class T, std::enable_if_t<decltype(has_arrow<T>(nullptr))::value, std::nullptr_t> = nullptr>
auto arrow_helper(T&& t) {
  return arrow_helper(std::forward<T>(t).operator ->());
}

template <class T, std::enable_if_t<!decltype(has_arrow<T>(nullptr))::value, std::nullptr_t> = nullptr>
auto arrow_helper(T&& t) {
  return &(std::forward<T>(t));
}

template <class T>
auto arrow_helper(T* t) {
  return arrow_helper(*t);
}
```

後は、ラッパークラスのメンバーアクセス演算子でarrow_helperを使えばよい。以下に全体のコードを示す。依然としてCのメンバーアクセス演算子がBのポインタを返しているにも係わらず、Holder<D>がAのメンバ変数valueに透過的にアクセス出来る事が分かる。

```cpp
#include <iostream>

template <class T, class U = decltype(std::declval<T>().operator ->())>
std::true_type has_arrow(std::enable_if_t<!std::is_same<std::decay_t<T>, std::decay_t<std::remove_pointer_t<U>>>::value, std::nullptr_t>);

template <class T>
std::false_type has_arrow(...);

template <class T, std::enable_if_t<decltype(has_arrow<T>(nullptr))::value, std::nullptr_t> = nullptr>
auto arrow_helper(T&& t) {
    return arrow_helper(std::forward<T>(t).operator ->());
}

template <class T, std::enable_if_t<!decltype(has_arrow<T>(nullptr))::value, std::nullptr_t> = nullptr>
auto arrow_helper(T&& t) {
std::cout << typeid(t).name() << std::endl;
    return &(std::forward<T>(t));
}

template <class T> auto arrow_helper(T* t) {
    return arrow_helper(*t);
}

struct A {
  A* operator ->() { std::cout << typeid(this).name(); return this; }
  int value;
};

struct B {
  A& operator ->() { std::cout << typeid(this).name(); return a; }
  A a;
};

struct C {
  B* operator ->() { std::cout << typeid(this).name(); return &b; }
  B b;
};

struct D {
  C& operator ->() { std::cout << typeid(this).name(); return c; }
  C c;
};

template <class T>
struct Holder {
  auto operator ->() { return arrow_helper(value_); }
  T value_;
};

int main() {
  Holder<Holder<Holder<D>>> h {};
  h->value = 42;
  std::cout << h.value_.value_.value_.c.b.a.value << std::endl; // 42
}
```

以上