2015-02-09 6 views
7

Ich versuche, Kompilierungszeit-Polymorphismus mit CRTP zu implementieren, und möchte die abgeleitete Klasse die Funktion implementieren implementieren.Emuliert reine virtuelle Funktion in statischen Polymorphie mit CRTP möglich?

Die aktuelle Implementierung ist so.

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

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

In dieser Implementierung aufrufen, um die Funktion in eine Endlosschleife fällt, wenn die abgeleitete Klasse nicht f() geführt hat.

Wie erzwinge ich die abgeleitete Klasse, um die Funktion wie reine virtuelle Funktion zu implementieren? Ich habe versucht, 'static_assert' wie static_assert(&base::f != &Derived::f, "...") zu verwenden, aber es erzeugt eine Fehlermeldung, die besagt, dass zwei Mitgliedsfunktionszeiger, die auf die Mitgliedsfunktionen der verschiedenen Klassen zeigen, nicht vergleichbar sind.

+0

Werfen Sie einen Blick auf 'ctype :: scan_is' und' ctype :: do_scan_is'. – Mehrdad

Antwort

5

Sie können die Sache geben Sie außer Kraft setzen und die Haken verschiedenen Namen, wie folgt aus:

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

Hier fimpl = delete in der Basis, so dass sie nicht versehentlich aufgerufen werden können, es sei denn, fimpl in der abgeleiteten Klasse überschrieben wird.

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

Die zweite Lösung, die eine Zwischenklasse setzt, ist wunderbar. – kukyakya

+0

Ich würde es auf die erste Art tun. Der zweite Weg riskiert ein F-up, wenn Sie vergessen, von der Zwischenklasse zu erben. – tmyklebu

+0

Ich denke, das ist nur ein Problem mit der Namensgebung. Wie wäre es, die reale Basis in den Namespace 'detail' zu setzen und die Zwischenklasse' base' zu ​​benennen? – kukyakya

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

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

Wenn die abgeleitete Klasse nicht implementiert f auf seine eigene, die Art der &Derived::f:

Sie können auch eine Zwischen Deckschicht in Ihr CRTP auf „vorübergehend“ mark f als delete Stick wäre void (Base::*)(), die Kompilierung bricht.

Seit C++ 11 können wir diese Funktion auch generisch mit variadic Vorlage machen.

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