2015-02-09 27 views
7

我想使用CRTP来实现编译时多态,并且想要强制派生类来实现函数。正在使用CRTP模拟静态多态中的纯虚函数吗?

当前的实现是这样的。

template <class Derived> 
struct base { 
    void f() { 
     static_cast<Derived*>(this)->f(); 
    } 
}; 

struct derived : base<derived> 
{ 
    void f() { 
    ... 
    } 
}; 

在此实现,调用函数陷入无限循环,如果派生类没有实现f()

如何强制派生类实现像纯虚函数一样的函数?我尝试使用'static_assert',如static_assert(&base::f != &Derived::f, "..."),但它生成一条错误消息,指出指向不同类的成员函数的两个成员函数指针不具有可比性。

+0

查看'ctype :: scan_is'和'ctype :: do_scan_is'。 – Mehrdad 2015-02-09 06:02:40

回答

5

你可以给你忽略的东西和钩不同的名称,如:

template <class Derived> 
struct base { 
    void f() { 
     static_cast<Derived*>(this)->fimpl(); 
    } 
    void fimpl() = delete; 
}; 

struct derived : base<derived> { 
    void fimpl() { printf("hello world\n"); } 
}; 

这里,fimpl = delete在底座,使得它不能被意外除非fimpl在派生类中重写调用。

您也可以坚持中间隐藏层到您的CRTP为“暂时”标记fdelete

template <class Derived> 
struct base { 
    void f() { 
     static_cast<Derived*>(this)->f(); 
    } 
}; 

template <class Derived> 
struct intermediate : base<Derived> { 
    void f() = delete; 
}; 

struct derived : intermediate<derived> { 
    void f() { printf("hello world\n"); } 
}; 
+0

放置中级班的第二种解决方案非常棒。 – kukyakya 2015-02-09 06:23:56

+0

我会这样做的第一种方式。如果忘记继承中级课程,第二种方法会冒F-up的风险。 – tmyklebu 2015-02-09 06:26:39

+0

我想这只是一个命名问题。把真正的基础放入'detail'命名空间并命名中间类'base'呢? – kukyakya 2015-02-09 06:41:08

1
template<typename Derived> 
class Base 
{ 
    private: 
    static void verify(void (Derived::*)()) {} 

    public: 
    void f() 
    { 
     verify(&Derived::f); 
     static_cast<Derived*>(this)->f(); 
    } 
}; 

如果派生类没有对自己实施f&Derived::f类型将是void (Base::*)(),这会中断编译。

既然C++ 11,我们也可以通过variadic模板使这个函数具有通用性。

template<typename Derived> 
class Base 
{ 
    private: 
    template<typename T, typename...Args> 
    static void verify(T (Derived::*)(Args...)) {} 
};