使用するメンバ関数をクラスに通知する(ようにみせかける)方法
このコードを見てほしい
printは使用するメンバ関数を出力し、funcは適当な処理を行う
int main(){ Foo foo; foo.print(); //func1 func2 func3 foo.func1(); foo.func3(); foo.func2(); }
出力>>func1 func2 func3
int main(){ Foo foo; foo.print(); //func2 func3 //foo.func1(); foo.func3(); foo.func2(); }
出力>>func2 func3
int main(){ Foo foo; foo.print(); //func3 //foo.func1(); foo.func3(); //foo.func2(); }
出力>>func3
これら3つのソースコードでFooの変更はしていない
また、funcが出力しているわけではない
しかしprintはこれから使用される関数を知っているかのように出力する……
答え
テンプレートで関数のインスタンス化を抑制し
関数内でstatic変数をもつテンプレートクラスを生成し
static変数はコンストラクタで適当に処理を行い
あたかもこれから使われる関数を知っているかのように見せかける
template<class T> class Piyo { public: void print() { for (auto&x : get()) { std::cout << x << std::endl; } } static std::vector<std::string>&get() { static std::vector<std::string> p; return p; } template<std::string(*CALL)()> struct Hoge{ Hoge(){x;} struct XXX{ XXX(){ get().push_back(CALL()); } }; static XXX x; }; void func1(){Hoge<_1> c;} void func2(){Hoge<_2> c;} void func3(){Hoge<_3> c;} static std::string _1(){ return"func1"; } static std::string _2(){ return"func2"; } static std::string _3(){ return"func3"; } }; template<class T>template<std::string(*CALL)()> typename Piyo<T>:: template Hoge<CALL>::XXX Piyo<T>::Hoge<CALL>::x; using Foo = Piyo<int>;
関数の生成を抑制すれば使われない関数は実体化しない
インスタンス化した関数はテンプレートクラスをインスタンス化
インスタンス化したテンプレートクラスはstatic変数を持ち
static変数はコンストラクタで色々悪さする
というギミック。使い道は現状ない
ラムダ式をテンプレート引数にできるようになればもうすこしスマートに書けるし
関数内で明示的なテンプレートのインスタンス化できればもう少し気持ちい感じになるかもしれない
ちなみにVisualStudio2013でのみ動作を確認
ギミック的にはほかのコンパイラでも動きそうな気がするが他のコンパイラだと
template<class T>template<std::string(*CALL)()> typename Piyo<T>:: template Hoge<CALL>::XXX Piyo<T>::Hoge<CALL>::x;
の部分がひっかかって構文エラーを起こす。こんな構文普段書かないから何が正解かさっぱりわからん
またhogeコンストラクタのxは削除するとXXXがインスタンス化されない
上の部分のC++的な正しい書き方、知ってる人がいらっしゃいましたら是非教えてください