2015-02-09 11 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

ответ

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 «временно» марка f в delete:

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

+0

Я бы сделал это первым путем. Второй способ - это риск, если вы забудете унаследовать от промежуточного класса. – tmyklebu

+0

Я думаю, это просто проблема именования. Как разместить реальную базу в пространстве имен 'detail' и назвать промежуточный класс' base'? – kukyakya

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 мы также можем сделать эту функцию общей с вариационным шаблоном.

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