2015-05-14 10 views
6

Mam następującą klasę podstawowego szablonu.Jak wymusić użycie ciekawie powtarzającego się wzorca szablonu w C++

template<typename T> 
class Base { 
    public: 
    void do_something() { 
    } 
}; 

jest przeznaczony do stosowania jako co ciekawe powtarzające się szablon wzoru. Powinien być dziedziczony jak class B : public Base<B>. Musi być , a nie odziedziczony jak class B : public Base<SomeoneElse>. Chcę statycznie egzekwować ten wymóg. Jeśli ktoś użyje tego błędu, oczekuję błędu w fazie kompilacji.

Co robię, umieszczam static_cast<T const&>(*this) w do_something(). W ten sposób klasa dziedzicząca szablon jest dziedziczona z klasy podanej jako parametr szablonu. Przepraszamy za mylące wyrażenie. W prostym angielskim wymaga B lub dziedziczy po SomeoneElse w class B : public Base<SomeoneElse>.

Nie wiem, czy jest to optymalny sposób, aby to osiągnąć. Wygląda obrzydliwie dla mnie.

Jednak chcę zrobić więcej. Chcę się upewnić, że B jest sam. Jak mogę to zrobić?

+0

'static_cast' nie wymusza niczego; powoduje jedynie niezdefiniowane zachowanie, jeśli '* this' nie jest w rzeczywistości' T'. –

+0

@ T.C. Wykonuje kontrole statyczne. Ten, o którym mówisz, powinien być "reinterpret_cast". –

+0

Sprawdza tylko, czy 'T' pochodzi od' Base ', a nie' * this' to 'T'. tj., biorąc pod uwagę 'class Bar: public Base {}; klasa Foo: publiczna baza {}; ', twój czek niczego nie złapie. –

Odpowiedz

8

Utwórz konstruktor (lub destruktor) z Base jako prywatny, a następnie zmień T na friend. W ten sposób jedyną rzeczą, która może skonstruować/zniszczyć Base<T> jest T.

+0

Proszę odnieść się do http://stackoverflow.com/questions/702650/making-a-template-parameter-a-friend. Wygląda na to, że nie możesz zaprzyjaźnić się z parametrem szablonu. –

+0

@Hot.PxL Najwyżej głosowana odpowiedź w tym pytaniu wyjaśnia, jak to zrobić na końcu ('friend T;', a nie 'friend class T;'). –

+0

Masz rację. Ale czy jest jakiś sposób, aby to zrobić, gdy C++ 11 nie jest dostępny? –

3

Jeśli klasa zawiera jakiś kod, który mówi:

T* pT = 0; 
Base *pB = pT; 

Wtedy nie będzie to błąd kompilatora, jeśli nie jest przyporządkowanie T kompatybilne z Base.

Ten rodzaj czeku jest sformalizowana w C++ 11, dzięki czemu nie trzeba pisać ręcznie i może się pomocne komunikaty o błędach:

#include <type_traits> 

template<typename T> 
class Base { 

public: 
    void do_something() 
    { 
     static_assert(
      std::is_base_of<Base, T>::value, 
      "T must be derived from Base"); 
    } 
}; 

class B : public Base<B> { }; 

int main() 
{ 
    B b; 
    b.do_something(); 
} 

chodzi o zapewnienie, że parametr typu Base „s jest dokładnie klasa wywodząca się z tego, co wydaje się błędne koncepcyjnie. Klasa, która działa jako klasa podstawowa, nie może "mówić" o typie, który ją dziedziczy. Może być dziedziczony więcej niż jeden raz przez wielokrotne dziedziczenie lub wcale.

+0

Sądzę, że spełnia tylko jedną stronę równania. Chcę również upewnić się, że 'B' jest dokładnie' SomeoneElse'. –

+0

To nie we wszystkich przypadkach na clang3.6 narzeka, że ​​klasa pochodna jest niekompletna podczas static_assert. –

+0

@RichardHodges Może zależeć od tego, gdzie wstawisz 'static_assert'. Dodałem kompletny przykład, który wydaje się dobry w MSVC 2013. Wypróbuję go na najnowszym klangu, gdy dostanę szansę. –

1

Dwie dobre odpowiedzi do tej pory. Tutaj jest inny, który używa idiomu generowania niestandardowych kluczy dostępu do niektórych metod (w tym przypadku konstruktora). Zapewnia absolutną gwarancję prawidłowego użytkowania, nie narażając prywatnych metod w bazie na pochodne.

Może być również używany do kontrolowania dostępu do innych metod w klasie bazowej dla każdego przypadku z osobna.

template<class Derived> 
struct Base 
{ 
private: 
    // make constructor private 
    Base() = default; 
protected: 
    // This key is protected - so visible only to derived classes 
    class creation_key{ 
     // declare as friend to the derived class 
     friend Derived; 
     // make constructor private - only the Derived may create a key 
     creation_key() = default; 
    }; 

    // allow derived class to construct me with a key 
    Base(creation_key) 
    {} 

    // other methods available to the derived class go here 

private: 
    // the rest of this class is private, even to the derived class 
    // (good encapsulation) 
}; 

struct D1 : Base<D1> 
{ 
    // provide the key 
    D1() 
    : Base<D1>(creation_key()) 
    {} 

}; 
Powiązane problemy