C++ではオーバーロードできる演算子が規定されている.

http://en.cppreference.com/w/cpp/language/operator_member_access

メンバアクセスのために用意された多くの演算子はオーバーロード可能であるため,たとえば

1
2
3
4
5
6
7
8
class B {
int n;
};
class A{
B b_;
B* operator->(){ return &b_; }
}

という風にアロー演算子をオーバーロードしてやれば,

1
2
A a;
a->n; // == A.b_.n

B のメンバの A のメンバを直接 B のメンバであるかのように触れたりする.

ここまではいいのだが,オーバーロードできない演算子である a.b を変更したいということがあった.
たとえば以下のようなコード:

1
2
3
4
5
6
7
8
int foo;
class C {
// what should I do?
};
C c;
c.foo; // desired: foo

メンバアクセスの . 演算子をオーバーロード出来ないので,これはちょっとむずかしい.
経緯は不明だが,この演算子はメモリ上で(構造体の先頭から)連続したアドレスにアクセスすることを一般的に期待するため,オーバーロードが出来ないのではないかと思う.
アロー演算子はポインタのメンバアクセスを示すため,メモリ上で連続していることを意味しないため,オーバーロード可能にしてあるのだろう.

だが,ここまでの要望は参照型 を用いれば容易に実現できる.

1
2
3
4
5
6
7
class C {
int& foo;
C(int& f): foo(f){}
}
C c(foo);
c.foo // foo

参照型char*&は実質的にはconst char**と同じであり,ただし . 演算子でアクセスできるインタフェースを提供するものである.これは便利.

しかし,今回はさらにやりたいことがあった.
それは,メモリ上で離れた位置のオブジェクトに,.演算子でメンバアクセスし,かつそのアドレスは適宜変更される というもの.
具体的には,STLコンテナに準拠したコンテナを実装している際に,std::pair と同じインタフェースのものを実装したかった.

以下が例:

1
2
3
std::pair<int, int> pair;
pair.first // Address: &pair ~ &pair+4
pair.second // Address: &pair+4 ~ &pair+8

これと同じインタフェースで,しかし first second にアクセスした際にはバラバラなアドレスを返せるようにしたい.しかし, 参照型は実装としてconstなので再代入ができない

1
2
3
4
5
6
7
8
9
10
11
12
13
int glob_foo;
class C {
int& foo;
C(int& f): foo(f){}
}
C c(glob_foo);
c.foo // foo
c.foo = 2; // NG. equivalent to glob_foo = 2;
int glob_bar;
c.foo = &glob_bar; // NG. const int* can't reassign

しかし,私は std::pair のようなインタフェースを持つイテレータを実装したかった. first second をメンバとして持ち,しかしこれにアクセスした際には別のメモリ領域を参照しに行きたかった.

そこでplacement new(配置new)を用いて解決した.

1
2
3
4
5
6
7
8
9
10
11
12
13
int glob_foo;
class C {
int& foo;
C(int& f): foo(f){}
}
C c(glob_foo);
c.foo // foo
int glob_bar;
new (&c) C(glob_bar); // placement-new
c.foo; // OK. glob_bar

以上です