続: 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; }