constexprでテンプレートメソッドパターンやろうとしたら、よくわからんくなった話
そもそもテンプレートメソッドパターンとは、以下のようにアクセスするpublicのメンバ関数内で、(純粋)仮想関数などを使用し、決められた型にはまった動作を行うようにするデザインパターンです。
class Original { protected: int m_num; virtual int cal()const=0; public: Original(const int num): m_num(num) {} void value()const { cout << "値は:"<< this->cal()<<endl; } }; class Square : public Original { private: int cal() const override { return m_num*m_num; } public: Square(const int num): Original(num) {} }; //後で比較するためにあえてここに作る const Square num(2); void main() { num.value(); }
これの場合は
- 「値は」と出力
- 何かわからんがcal関数で計算された値が出力
というだけの枠組みに決まったvalue()関数である。
ですがconstexprを学びだした僕はこう思ったわけです
ん?おいまてよ、これくらいならconstexprにしたいぞと、
ってことでconstexprにしてみる
class Original { protected: int m_num; virtual int hoge()const=0; public: constexpr Original(const int num): m_num(num) {} void value()const { cout << "値は:"<< this->hoge()<<endl; } }; class Square : public Original { private: int hoge() const override { return m_num*m_num; } public: constexpr Square(const int num): Original(num) {} }; constexpr Square num(2); void main() { num.value(); }
やってやったぞ!優勝!!!と思ったあなたは大間違い
これを実行すると…動作が停止します。
thisがねえって怒られます。
constexprはvirtualメソッドがNGって聞きます。つまりはダメ
ちなみに下みたいにしたら問題なく動いたのですが、その理由がわからない(T T)
void main() { constexpr Square num(2); num.value(); }
h内に書いたりした場合ももちろん動作は停止。
では、どうやって解決するかですが
templateを使って静的ポリモーフィズムのようにします
template<class _T> class Original { protected: int m_num; public: constexpr Original(const int num): m_num(num) {} void value()const { cout << "値は:"<< static_cast<const _T&>(*this).cal()<<endl; } }; class Square : public Original<Square> { friend class Original<Square>; private: int cal() const { return m_num*m_num; } public: constexpr Square(const int num): Original(num) {} };
おいおいfriendがでてきたぞと、
friendとはそのclassに自身のprivateやprotectedのメンバへのアクセスを許可するものです。
hogeをpublicにすればいいのですが、それではテンプレートメソッドを使ってるメリットがなくなってしまいます。
これでOKですが…
Originalにhogeさえ持ってればどんなclassでも_Tに渡せる状態なのでわりと危険かな
(static_castしてるのでそこでエラーは、はいてくれるが)
templateを使うたびにやばいこと(危険なこと)してる気がしてます。