Rokiのチラ裏

学生による学習のログ

CRTPによる静的インターフェースクラスを用いた実装でインスタンス化せずにコンパイル時エラーを吐かせる

CRTPによる静的インタフェースクラスによって派生クラスでの該当関数の実装を強要する。

template<class _Tp>
struct base{
    void f()const{static_cast<_Tp>(this)->f();}
};
struct derived:private base<derived>{
    void f()const{}
};

int main()
{
    derived().f();
}

しかし、使わなければ実体化しないためコンパイルエラーは吐かれない。

template<class _Tp>
struct base{
    void f()const{static_cast<_Tp>(this)->f();}
};
struct derived:private base<derived>{};

int main()
{
    derived();
}

Qiitaに同じ問題点を挙げているエントリがある:【C++】CRTPによる静的インターフェースクラスはライブラリ実装において不適切+解決法 - Qiita
このエントリにあるようにstatic_assertを用いるのは良いと思われるが、このエントリで示されているコードはゼロオーバヘッドではない。また、指定したい関数の書式をis_sameで構成しているため直感的ではない。以下に適切な処置法を示す。

解決策

#include<type_traits>

template<class _Tp,void (_Tp::*)()>
struct signature_f_1{
    using type=_Tp;
};

template<class _Tp,class=_Tp>
struct signature_f_2:std::false_type{};
template<class _Tp>
struct signature_f_2<_Tp,typename signature_f_1<_Tp,&_Tp::f>::type>:std::true_type{};

template<class _Tp>
constexpr bool signature_f_v=signature_f_2<_Tp>::value;

template<class Derived>
struct base{
    virtual ~base()=default;
    base(){static_assert(signature_f_v<Derived>,"u must define function f");}
    void f(){return static_cast<Derived*>(this)->f();}
};

継承先でfを作らないと

struct derived:base<derived>{};
int main()
{
    derived();
}

怒られる。

エラー: static assertion failed: u must define function f
  base(){static_assert(signature_f<Derived>::value,"u must define function f");}

signature_f_1でのtemplate arguments部分で関数ポインタを書くだけなのでより直感的と言える。