2010-12-11 2 views
15

Использование CRTP иногда я пишу такой код:Как избежать ошибок при использовании CRTP?

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

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

И это очень трудно понять, что идет не так, пока я не трассировать код в отладчике и видеть, что члены Бара не используются в Base.

Как выявить эту ошибку во время компиляции?

(я использую MSVC2010, так что я могу использовать некоторые C++ 0x функции и расширения MSVC языка)

ответ

12

В C++ 0x у вас есть простое решение. Однако я не знаю, реализована ли она в MSVC10.

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

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

на самом деле это не работает, если dtor никогда не вызывается. – Abyx

+0

@Abyx: Интересно, что gcc 4.9, если я использую новое размещение для создания объекта типа 'struct S: base {}', он жалуется на то, что * конструктор * 'S :: S()' неявно удален из-за '~ base' является закрытым. Однако в этом случае деструктор никогда не вызывается. –

+0

Более интересно, gcc 4.8.1 не жалуется вообще! –

0

Там нет никакого способа узнать тип, производный. Вы можете применить этот код Foo, полученный от Base<Foo>, но вы не можете обеспечить, чтобы из этого не вытекали другие классы.

0

я могу использовать макрос

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

, но я не хочу использовать макросы, если лучшее решение существует.

10

Вы можете использовать что-то вроде этого:

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

Он становится действительно многословным, когда Foo сам является шаблоном. –

+3

@ Александр: шаблоны являются подробными. – Amnon

+0

Да, они есть, но в производственном коде ваш код становится сложнее использовать, чем простой CRTP (я однажды попытался использовать что-то вдоль этих строк по той же причине, что и 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 
}; 

Этот код основан на Amnon's answer, но код проверки не содержит имя производного класса, так что я могу скопировать и вставить его без изменений.