ユーザ定義変換演算子の戻り値型について
どうやらC++の変換演算子(operator ClassName())は、コンパイル時に戻り値の型をチェックしないっぽい。
例えば、
//BからAへの暗黙の型変換 struct A { }; struct B { operator A() { return A(); } }; int main() { A a; B b; a = b; }
上のコードは普通の暗黙変換。
ここで、例えば下のように間違ってoperator A()でB型を返してしまうと
struct A { }; struct B { operator A() { //Aへの変換演算子がBを返す return B(); } }; int main() { A a; B b; a = b; }
とすると、普通にコンパイルが通って実行時にB::operator A()がBを返し、
再帰的にB::operator A()を呼び出しまくるので、スタックオーバーフローで落ちる。
ちなみに、
struct A { }; struct C { }; struct B { operator A() { return C(); } }; int main() { A a; B b; a = b; }
とかすると、ちゃんとコンパイル時にa=bは変換できないよ!ってエラー吐いてくれる。
とりあえずこの仕様を使って、2つ以上のクラスを経由した暗黙の型変換をやってみた。
まず、普通に非explicitなコンストラクタで暗黙の型変換を行った場合
struct A { }; struct B { B(){} B(A){} }; struct C { C(){} C(B){} }; struct D { D(){} D(C){} }; int main() { A a; B b; C c; D d; //(1) d = b; }
(1)の部分で2つ以上のクラスを経由して暗黙の型変換を解決することは出来なのいので
コンパイルエラーになる。
これを、変換演算子が返値の型をチェックしないっぽいことを利用して書いてみると
struct D { }; struct C { operator D() { return D(); } }; struct B { operator D() { return C(); } }; struct A { operator D() { return B(); } }; int main() { A a; D d; d = a; }
こんな感じになって、普通に動く。
まあすべてのクラスがoperator A()を持っているので、ある意味では1ホップの変換と見ることも出来るんだけど
途中で一時インスタンスが2つ以上生成されているので、ここではとりあえず暗黙の型変換と言うことで。
ちなみにこの仕様に気づいたのが、テンプレート使いまくりのポリシークラス間変換だったので、
実は原因を見つけるのにすごく時間がかかったり・・・
しかしなんで変換演算子が返す型をチェックをしない仕様になってるのか正直よくわからないので、
誰か知ってる人いたら教えて!