2015-02-09 8 views
7

Sto cercando di implementare il polimorfismo in fase di compilazione utilizzando CRTP e voglio forzare la classe derivata ad implementare la funzione.È possibile emulare la funzione virtuale pura nel polimorfismo statico utilizzando CRTP?

L'implementazione corrente è come questa.

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

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

In questa implementazione, chiamata alla funzione cade in un ciclo infinito se la classe derivata non ha attuato f().

Come impongo alla classe derivata di implementare la funzione come pura funzione virtuale? Ho provato ad usare "static_assert" come static_assert(&base::f != &Derived::f, "...") ma genera un messaggio di errore che dice che due puntatori funzione membro che puntano alle funzioni membro delle diverse classi non sono confrontabili.

+0

Dai un'occhiata a 'ctype :: scan_is' e' ctype :: do_scan_is'. – Mehrdad

risposta

5

Si può dare la cosa di ignorare e il gancio nomi diversi, come questo:

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"); } 
}; 

Qui, fimpl = delete nella base in modo che non può essere chiamato per errore a meno che fimpl viene sostituita nella classe derivata.

È possibile anche attaccare uno strato nascondiglio intermedio nella vostra CRTP per "temporaneamente" marchio f come 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

La seconda soluzione per mettere una classe intermedia è meravigliosa. – kukyakya

+0

Lo farei per primo. Il secondo modo rischia un F-up se si dimentica di ereditare dalla classe intermedia. – tmyklebu

+0

Immagino che sia solo un problema di denominazione. Che dire mettere la vera base nel namespace 'detail' e nominare la' base' della classe intermedia? – kukyakya

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

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

Se la classe derivata non implementa f da sola, il tipo di &Derived::f sarebbe void (Base::*)(), che interrompe la compilazione.

Dal C++ 11 possiamo anche rendere questa funzione generica con modello variadic.

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