続: C++で仮想メンバ関数テンプレートを使いたい話

C++ で仮想メンバ関数テンプレートを使いたい - kazuhoのメモ置き場 の続き。

結局、関数の引数によるコンパイル時の多態性 (m) と、仮想関数による実行時の多態性 (n) を組み合わせようとした瞬間に、メンバ関数テンプレートが使えなくなって、必要な定義量が m*n に爆発してしまうのを防ぐいい方法はないのか、って話なわけで。

で、昨日書いた if (typeid(*this) ...) する方法は、n が既知の場合に、定義の量を n で済ませる手法になる。一方で、m が既知の場合に定義の量を m+n で済ませる方法もあるな、と思って、こんなの書いてみた。

引数が取りうる型を、テンプレートクラス Base::any_print_t で全部書いておいて、それを、それぞれの実装クラスで CRTP っぽく実体化させるアプローチ。取りうる型の定義が足りなければ、コンパイルエラーになるので、適宜追加していけばいい。

#include <cassert>
#include <iostream>

class Base {
public:
  virtual ~Base() {}
  // the API
  template <typename T> void any_print(const T& v) {
    get_any_print()(this, v);
  }
  // declare instantiation patterns as operator()s
  template <typename Klass, typename Super = Base>
  struct any_print_t : public Super::any_print_base_t {
    virtual ~any_print_t() {}
    virtual void operator()(Base* b, int v) const {
      return static_cast<Klass*>(b)->any_print_stub(v);
    }
    virtual void operator()(Base* b, const std::string& v) const {
      return static_cast<Klass*>(b)->any_print_stub(v);
    }
  };
  // base class definitions
  struct empty_class { struct any_print_base_t {}; };
  typedef any_print_t<Base, empty_class> any_print_base_t;
  virtual const any_print_base_t& get_any_print() const = 0;
  // template function to be called
  template <typename T> void any_print_stub(const T& v) {
    assert(0); // never called
  }
};

class Derived : public Base {
public:
  virtual const Base::any_print_base_t& get_any_print() const { 
    static const Base::any_print_t<Derived> s;
    return s;
  }
  template <typename T> void any_print_stub(const T& v) {
    std::cout << "Derived:" << v << std::endl;      
  }
};

class Derived2 : public Derived {
public:
  virtual const Base::any_print_base_t& get_any_print() const {
    static const Base::any_print_t<Derived2> s;
    return s;
  }
  template <typename T> void any_print_stub(const T& v) {
    std::cout << "Derived2:" << v << std::endl;      
  }
};

int main(void)
{
  Base* b = new Derived();
  b->any_print(1);
  b->any_print("hello");
  delete b;
  
  b = new Derived2();
  b->any_print(2);
  b->any_print("ciao");
  delete b;
  
  return 0;
}