タイトルはどうでも良い!!
要するに名前が同じで引数が違うメンバ関数は継承先で引き継がれないよ、ということ。
どういうことかと言うと、
class A { public: void setTarget(A* a) { a_ = a; } private: A* a_; };
こんなのがあるとする。
ターゲットとしてA*を持つ。
が、これを継承したBでは、ターゲットとしてベクトルを持ちたくなったとする。
すると当然こうする。
class Vector2D { public: Vector2D(int x, int y) : x_(x), y_(y) {} Vector2D() : x_(0), y_(0) {} private: int x_; int y_; }; class B : public A { public: void setTarget(const Vector2D& v) { v_ = v; } private: Vector2D v_; };
よし、これで、
setTarget(A* a);
setTarget(const Vector2D& v);
が使えるようになった筈だと意気揚々とする。
int main() { Vector2D v(100, 100); A* a = new A(); B* b = new B(); a->setTarget(b); b->setTarget(a); // あれ……コンパイル通らないよ! b->setTarget(v); delete a; delete b; return 0; }
そう。
setTargetがBで宣言された事によって、
AのsetTargetは使えなくなってしまった。
オーバーロード、なにしてんの!!!!
とか突っ込むべきところか突っ込まないべきところかは解らないけれど、
とにかく通らない。
これはusingして解決する。
class B : public A { public: using A::setTarget; void setTarget(const Vector2D& v) { v_ = v; } private: Vector2D v_; };
using A::setTarget;の魔法で解決する。
が、実際、自分はこれで困ったことがない。
なんで、今回書こうかと思ったかというと
数年前C++ Labyrinthを最初読んだときにピンとこなかったからだ。
これは、おそらく、C++ プログラマなら誰しも驚いた経験していることであり、 もし、あなたが未経験であってこの文章を読んで知識を仕入れておいたとしても、 きっといつかは体験して驚くことになるであろう、C++ の一仕様の話である。
いささか仰々しい書き出しになったが、今回の話題は、 「基底クラスと派生クラスで同じ名前の関数が定義されていた場合、 基底クラスの関数は隠されてしまう」という、C++ の仕様のことである。
ここで、「同じ名前なら隠されるのは当り前やんか」と思ったあなたは、 ちょっと読みが甘い。「同じ名前」というのは、文字通り、 「関数名だけが一致している」 ということであって、引数など、 シグネチャを構成する他の要素は違っていても構わないのだ。
C++ Labyrinth
確かによく考えたらおかしい。
でも、まぁ、間違って意図しないものが呼ばれるよりは万倍マシだ。
なぜなら、基底がintで継承後がfloatだった場合、酷いことになる。
class C { public: void foo(int value) { printf("C:foo(int)\n"); } }; class D : public C { public: using C::foo; // <- これがもし暗黙だったなら!!! void foo(float value) { printf("D:foo(float)\n"); } };
こういうのを考えてみる。
using が暗黙であった場合、
D* d = new D(); d->foo(100);
は残念ながら、
D::foo(float)
ではなく
C::foo(int)
を呼び出す。
これは多くの場合、悲惨な結末をもたらす。
そういう意味では、
C++の自己防衛本能としての「オーバーロードは継承しない」なんだろう。
泥臭いけど。
D&Eにも載ってたと思うけど。