2015-02-09 8 views
7

Próbuję wprowadzić polimorfizm czasu kompilacji za pomocą protokołu CRTP i chcę wymusić na klasie pochodnej implementację tej funkcji.Czy emulacja czystej funkcji wirtualnej w statycznym polimorfizmie za pomocą CRTP jest możliwa?

Obecna realizacja wygląda następująco.

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

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

W tej implementacji, wywołanie funkcji wpada w nieskończoną pętlę jeśli klasa pochodna nie wdrożył f().

Jak wymusić wyprowadzoną klasę, aby zaimplementowała funkcję podobną do czystej funkcji wirtualnej? Próbowałem użyć "static_assert", jak static_assert(&base::f != &Derived::f, "..."), ale generuje on komunikat o błędzie informujący, że dwa wskaźniki funkcji członków wskazujące funkcje członków różnych klas nie są porównywalne.

+0

Spójrz na 'ctype :: scan_is' i' ctype :: do_scan_is'. – Mehrdad

Odpowiedz

5

Można dać coś nadpisać i haka różne nazwy, na przykład:

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

Tutaj fimpl = delete w bazie tak, że nie można nazwać przypadkiem chyba fimpl jest przesłonięte w klasie pochodnej.

Można również przykleić warstwę pośrednią kryjówkę w swojej CRTP do „tymczasowo” znaku f jak 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

Drugie rozwiązanie klasy pośredniej jest wspaniałe. – kukyakya

+0

Zrobiłbym to pierwszy sposób. Drugi sposób naraża na ryzyko F-up, jeśli zapomnisz odziedziczyć po klasie pośredniej. – tmyklebu

+0

Myślę, że to tylko problem z nazywaniem. Co powiesz na umieszczenie prawdziwej bazy w przestrzeni nazw 'detail' i nazwę klasy pośredniej' 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(); 
    } 
}; 

Jeśli klasa pochodna nie implementuje f na własną rękę, typ &Derived::f będzie void (Base::*)(), który przerywa kompilację.

Od C++ 11 możemy również uczynić tę funkcję ogólną z szablonem variadic.

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