2010-12-11 14 views
15

Mit CRTP manchmal schreibe ich einen Code wie folgt:Wie vermeidet man Fehler bei der Verwendung von CRTP?

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

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

Und es ist sehr schwer zu verstehen, was schief geht, bis ich Code in Debugger verfolgen und sehen, dass nicht Bar-Mitglieder in Base verwendet werden.

Wie wird dieser Fehler zur Kompilierzeit angezeigt?

(ich benutze MSVC2010, so kann ich einige C++ 0x-Features und MSVC Spracherweiterungen verwenden)

Antwort

12

In C++ 0x haben Sie eine einfache Lösung. Ich weiß jedoch nicht, ob es in MSVC10 implementiert ist.

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

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

eigentlich funktioniert es nicht, wenn der dtor nie aufgerufen wird. – Abyx

+0

@Abyx: Interessanterweise mit GCC 4.9, wenn ich Placement neu verwenden, um ein Objekt des Typs 'Struktur S: Basis {}' zu konstruieren, beschwert sich über den * Konstruktor * 'S :: S()' implizit gelöscht werden '~ base' ist privat. In diesem Fall wird der Destruktor jedoch nie aufgerufen. –

+0

Interessantererweise klagen gcc 4.8.1 überhaupt nicht! –

0

Es gibt keine Möglichkeit zu wissen, den Ableitungstyp. Sie könnten dies erzwingen Foo abgeleitet von Base<Foo>, aber Sie können nicht erzwingen, dass keine anderen Klassen auch davon abgeleitet werden.

0

kann ich ein Makro

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

verwenden, aber ich will nicht, Makros verwenden, wenn eine bessere Lösung existiert.

10

Sie können so etwas wie folgt verwenden:

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

Es wird sehr ausführlich, wenn Foo selbst eine Vorlage ist. –

+3

@Alexandre: Vorlagen sind ausführlich. – Amnon

+0

Ja, das sind sie, aber im Produktionscode wird Ihr Code schwieriger zu verwenden als das einfache CRTP (Ich habe einmal versucht, etwas in diese Richtung zu verwenden, aus dem gleichen Grund wie 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 
}; 

Dieser Code auf Amnon's answer basiert, aber Code Überprüfung tun Namen abgeleiteten Klasse nicht enthält, so kann ich es ohne Änderungen kopieren und einfügen.

Verwandte Themen