2010-12-11 19 views
15

Korzystanie CRTP czasami napisać kod jak poniżej:Jak uniknąć błędów podczas korzystania z protokołu CRTP?

// this was written first 
struct Foo : Base<Foo, ...> 
{ 
    ... 
}; 

// this was copy-pasted from Foo some days later 
struct Bar : Base<Foo, ...> 
{ 
    ... 
}; 

I to jest bardzo trudne do zrozumienia tego, co idzie źle, dopóki nie kod śledzenia debugger i zobaczyć, że członkowie bar nie są wykorzystywane w Base.

Jak ujawnić ten błąd podczas kompilacji?

(używam MSVC2010, więc mogę korzystać z niektórych C++ 0x funkcje i rozszerzenia języka msvc)

Odpowiedz

12

W C++ 0x masz proste rozwiązanie. Nie wiem jednak, czy jest on zaimplementowany w MSVC10.

template <typename T> 
struct base 
{ 
private: 
    ~base() {} 
    friend T; 
}; 

// Doesn't compile (base class destructor is private) 
struct foo : base<bar> { ... }; 
+0

w rzeczywistości nie działa, jeśli dtor nigdy nie jest wywoływany. – Abyx

+0

@Abyx: Interesująco z gcc 4.9, jeśli używam miejsca docelowego new do skonstruowania obiektu typu 'struct S: base {}', to narzeka na to, że * konstruktor * 'S :: S()' jest niejawnie usuwany z powodu '~ base' jest prywatne. Jednak w tym przypadku destruktor nigdy nie jest wywoływany. –

+0

Co ciekawsze, gcc 4.8.1 w ogóle nie narzeka! –

0

nie ma możliwości dowiedzenia typ wynikających. Możesz wymusić, że Foo pochodzi od Base<Foo>, ale nie można wymusić, że żadne inne klasy również nie pochodzą z tego.

0

mogę użyć makro

#define SOMENAMESPACE_BASE(type, arg1, arg2) type : Base<type, arg1, arg2> 

ale nie chcę używać makr czy lepszym rozwiązaniem nie istnieje.

10

Można użyć czegoś takiego:

template<class T> class Base { 
protected: 
    // derived classes must call this constructor 
    Base(T *self) { } 
}; 

class Foo : public Base<Foo> { 
public: 
    // OK: Foo derives from Base<Foo> 
    Foo() : Base<Foo>(this) { } 
}; 

class Moo : public Base<Foo> { 
public: 
    // error: constructor doesn't accept Moo* 
    Moo() : Base<Foo>(this) { } 
}; 

class Bar : public Base<Foo> { 
public: 
    // error: type 'Base<Bar>' is not a direct base of 'Bar' 
    Bar() : Base<Bar>(this) { } 
}; 
+0

To staje się bardzo szczegółowe, gdy sam Foo jest szablonem. –

+3

@Alexandre: szablony są pełne. – Amnon

+0

tak, ale w kodzie produkcyjnym twój kod staje się trudniejszy w użyciu niż zwykły CRTP (raz spróbowałem użyć czegoś wzdłuż tych linii z tego samego powodu co OP). –

2
template<typename T, int arg1, int arg2> 
struct Base 
{ 
    typedef T derived_t; 
}; 

struct Foo : Base<Foo, 1, 2> 
{ 
    void check_base() { Base::derived_t(*this); } // OK 
}; 

struct Bar : Base<Foo, 1, 2> 
{ 
    void check_base() { Base::derived_t(*this); } // error 
}; 

Kod ten jest oparty na Amnon's answer, ale sprawdzanie kodu nie zawiera nazwę klasy pochodnej, więc mogę skopiować i wkleić go bez zmian.

Powiązane problemy