読者です 読者をやめる 読者になる 読者になる

にほんごのれんしゅう

日本語として伝えるための訓練を兼ねたテクログ

C++の静的ポリモフィズム

C++の静的ポリモフィズム

モチベーション

Scalaの継承の概念にだいぶ体力を削られるという残念な事象が発生した. 今まで,継承をかなり軽んじている部分があったので,この際,C++ポリモフィズムについても再度,検討すべしと判断するに至った.
Scalaで苦労した点については,いずれ紹介したいと思う.

継承は悪なのか

Rubyの製作者のまつもとひろゆきに言わせると,一意見とすると,悪であるという見方もできるらしい.
私はRubyユーザでも信者でもないが,継承は使いにくいと思う.原因はいろいろあるが,仮想関数のvtable無しにダウンキャストを行っているコードを見かけてなんで動作しているのか,未だに理解できないのである.
vtable無しで動作する理由が,コンパイラのパーサが適宜,クラスを解釈してミックスインしているのならば,,,分かる,,,気がする,,,

C++11への静的ポリモフィズム

C++11とか言ってしまっていいのか迷うが,lambda式を利用しているので,とりあえずC++11での静的ポリモフィズムとさせてください.
動的ポリモフィズムでもいいのならば,コンストラクタの引数にlambdaを取るなどしてできるが,静的ポリモフィズムでできないか,,,と考えてみた.すなわち,C++03以前のパラダイムで定石と呼ばれている方法を,踏襲していない.

いかにして実装するか

ポリモフィズムの原点は,同じ関数名が呼ばれたときに別の実装系を呼び,多様性を確保することである.
ここで,多種多様なクラスを作らずに,template引数で静的にポリモフィズムを実装する.具体的には,関数ポインタ(のようなlambdaを)templateで受け取りポリモフィズムを実現する.

コード

以下がcppのコードである.c++11以降のシンタックスを利用している.c++03でもStructを使えばできないこともないと思う.

#include<vector>
#include<memory>
#include<functional>
class BBase{
  public:
  virtual void echo()=0;
};
template<std::function<void()>& F1>//<- capture lambda
class B : public BBase{
  public:
  void echo(){
    F1();
  };
  virtual ~B(){};
};
/** need to bind global parameters */
namespace BFUNCS{
  std::function<void()> f1a = [](){std::cout << "echo from f1!" << std::endl;};
  std::function<void()> f2b = [](){std::cout << "echo from f2!" << std::endl;};
  std::string s;
};
int main(int argc, char const *argv[])
{
  /**
   * @brief test of static polymorphism
   * */
  std::cout << "like duck type" << std::endl;
  std::vector<BBase*> v = { new B<BFUNCS::f1a>(),  new B<BFUNCS::f2b>() };
  std::for_each(v.begin(), v.end(), [](BBase* b){b->echo();});
  std::for_each(v.begin(), v.end(), [](BBase* b){delete b;});
}

実行結果は以下のようになる.

gkobayas@ubuntu:~/dev/YUTORI/YUTORI$ ./a.o
like duck type
echo from f1!
echo from f2!

結論:静的ポリモフィズムは簡単にできる

静的ポリモフィズムもそんなに難しくない.
IMPLパターンとみなすこともできるね.