C++における共変戻り値と反変引数

近々、wikiの「C++まにあっく」のほうに書きたい話の下書き。

共変戻り値と反変引数とは

オブジェクト指向において、共変、反変というと、こういうことです。

  • クラス HogeBase の派生クラスが HogeDerived
  • クラス FugaBase の派生クラスが FugaDerived

とします。このとき、

  • HogeBase → HogeDerived と変更するとき FugaBase → FugaDerived と変化するのが「共変」
  • 反対に FugaDerived → FugaBase と変化するのが「反変」

です。

C++の場合、メソッドの戻り値を共変にすることができます。つまり

class HogeBase {
    virtual FugaBase* method();
};
class HogeDerived : public HogeBase {
    virtual FugaDerived* method();
};

のように、戻り値が違っても共変であれば、HogeDerived::method() が HogeBase::method() をオーバーライドできます。
HogeDerived::method() がたとえ HogeBase::method() として実行されたとしても、戻り値を FugaDerived → FugaBase とアップキャストすれば良いだけなので、大丈夫というわけです。

一方、メソッドの引数を共変にすると、ダウンキャストが必要になるので、安全ではありません。安全なのは反変な引数です。

class HogeBase |
    virtual void method(FugaDerived*);
};
class HogeDerived : public HogeBase {
    virtual void method(FugaBase*);
};

たとえ HogeDerived::method() が HogeBase::method() として実行されたとしても、やはりアップキャストしか起こらないので安全です。

反変引数の使いどころ

ところが、C++は反変引数をサポートしていないようです。確かにあまり用途が見つからないので仕方ないかなと思っていましたが、ひとつ有用かもしれない場合を見つけました。

template で mix-in」というテクニックがあります。

template <typename T>
class MixIn : public T {
    // ...
};

というクラスを用意することで、任意のクラス T に必要な機能を追加する mix-in を作製できるというものです。
さて、ここで mix-in としては邪道な使い方かもしれませんが、この MixIn 内で

template <typename T>
class MixIn : public T {
    virtual void method(FugaBase*);
};

というメソッドを用意し、T::method() をオーバーライドすると仮定します。T は任意のクラスなのでオーバーライドするメソッドは

  • T::method(FugaDerived*)

かもしれませんし

  • T::method(FugaDerived2*)

かもしれません。

この場合には反変な引数が必要となります。
…無理やりだなあ。