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

にほんごのれんしゅう

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

C++とScalaの多相

C++Scalaの多相

モチベーション

人生は短い.ゆえに,多くの言語に手を出し,中身が薄く意味がないものになってしまうのは避けたい.
たとえば,二つ以上の言語パラダイムを学び知った気になっていると,言語仕様の拡張とともに,あっという間に過去の人,,,見たいになったりするので,要注意である.
ずっと前から思っていたことに,C++のtemplateの機能拡張と,型を重視する型論派の間でScala, Haskellが何気に似ているのではないかと思っていた.
二つのパラダイムを無理やり一つのパラダイムとみなして学習してしまえば楽,,,とかちょっと欲をだした.

Scalazで学んだ多相の一種

Haskellからの体系的な理解をすっ飛ばして,どう多相を実現しているかのみを抽出して,既存C++ template, Clojureの知識に当てはめて理解することにした.
Scalaの多相のやり方としてわかりやすかった例を載せておく.(ほかにも多相を実現する方法は多岐にわたっており,ほんの一部でしかないことに注意)

class Hage
class Ossan

trait Male[T] {
  def who(t:T)
  def whoAmI() = {
    println("arround 30")
  }
}
object Male{
  def fallInLove[T](t:T)(implicit m:Male[T]) = {
      m.who(t:T)
  }
  def WhoAmI[T](t:T)(implicit m:Male[T]) = {
    m.whoAmI()
  }
  implicit object HageMale extends Male[Hage]{
    def who(t:Hage) = {println("hage chirakashi-te gomen")}
  }
  implicit object OssanMale extends Male[Ossan]{
    def who(t:Ossan) = {println("i will be a storker!!!! ")}
  }
}

object MAIN {
  def main(args: Array[String]) = {
    Male.fallInLove(new Hage)
    Male.WhoAmI(new Hage)
    Male.fallInLove(new Ossan)
    Male.WhoAmI(new Ossan)
  }
}

こんな感じで多相を実現した.

この例であればC++でもできるとの確信を持ち,C++で書き直してみた.(下記の例は,DIとどう違うのと言われ,理解されることがなかった)

ただしgcc4.8を用いてC++11を有効にしてある.

std::function<void()> A30 = [](){std::cout << "arround 30" << std::endl;};
template<class T, std::function<void()>& WhoAmI>
class Male{
  public:
  void who() ;
  void whoAmI() {WhoAmI();};
};
class Hage;
class Ossan;
template<>
class Male<Hage, A30>{
  public:
  void who(){ std::cout << "Hage chirakashite gomen" << std::endl;};
  void whoAmI() {A30();}
};
template<>
class Male<Ossan, A30>{
  public:
  void who(){ std::cout << "I will be a stalker!!!" << std::endl;};
  void whoAmI() {A30();}
};
int main(int argc, char const *argv[])
{
  /**
   * @brief scala like polymorphism
   * */
  auto hage = std::shared_ptr<Male<Hage, A30>>(new Male<Hage, A30>);
  hage->who();
  hage->whoAmI();
  auto ossan = std::shared_ptr<Male<Ossan, A30>>(new Male<Ossan, A30>);
  ossan->who();
  ossan->whoAmI();
}

一部,無理くりScalaの記法に似せているが,まあこんなもんだろ.
やっぱり大体同じ動作をするではないか.

改訂版:DIにできるだけ似せていないパターン

twitterでいろいろ指摘していただいて,推定していないじゃないかと言われたので修正した.
template引数がコンストラクタに与える引数と似ているからいろいろ誤解を生んだ.

std::function<void()> A30 = [](){std::cout << "arround 30" << std::endl;};
template<class T, std::function<void()>& WhoAmI = A30>
class Male{
  public:
  void who() ;
  void whoAmI() {WhoAmI();};
};
class Hage{};
class Ossan{};
template<> //templateの記法上,このように書くしかない.
class Male<Hage>{
  public:
  static void who(){ std::cout << "Hage chirakashite gomen" << std::endl;};
  static void whoAmI() {A30();}
};
template<>
class Male<Ossan>{
  public:
  static void who(){ std::cout << "I will be a stalker!!!" << std::endl;};
  static void whoAmI() {A30();}
};
class MaleObject{
  public:
  template<class T> //推定部分,コードのあり方から,型を推定して処理を振り分ける
  static void fallInLove(T* t){
    Male<T>().who();
  }
  template<class T>
  static void WhoAmI(T* t){
    Male<T>().whoAmI();
  }
};
int main(int argc, char const *argv[])
{
  MaleObject::fallInLove(new Hage); //コンパイラの推定機能に頼って,推定されたインスタンスを引き出す テンプレート引数を省略することで推定させる
  MaleObject::WhoAmI(new Hage);
  MaleObject::fallInLove(new Ossan);
  MaleObject::WhoAmI(new Ossan);
}

これでどうっすか.