Erstens, wenn Sie Init und Destroy benötigen, bedeutet dies, dass der Vorlagencode sie irgendwo verwendet. Das bedeutet, dass ihr Vorhandensein bereits vom Compiler überprüft wird, da die Vorlage nicht kompiliert werden kann, wenn der Typ nicht über diese Methoden verfügt.
Wenn man jedoch nach ihnen suchen möchte, könnte eine Möglichkeit darin bestehen, ihre Adressen in einem Kontext zur Kompilierzeit zu verwenden, z.B.
template <class T>
class X
{
private:
template <unsigned N>
struct Number {};
Number<sizeof(&T::Init) + sizeof(&T::Destroy)> must_define_init_and_destroy();
};
struct A
{
bool Init();
void Destroy();
};
struct B {};
int main()
{
X<A>();
X<B>();
}
Bei Comeau ist die Ausgabe:
"ComeauTest.c", line 7: error: class "B" has no member "Init"
Number<sizeof(&T::Init) + sizeof(&T::Destroy)> must_define_init_and_destroy();
^
detected during instantiation of class "X<T> [with T=B]" at line 21
"ComeauTest.c", line 7: error: class "B" has no member "Destroy"
Number<sizeof(&T::Init) + sizeof(&T::Destroy)> must_define_init_and_destroy();
^
detected during instantiation of class "X<T> [with T=B]" at line 21
Dies funktioniert jedoch nicht, wenn eine der erforderlichen Methoden überladen wird, und natürlich wird auch nicht geprüft, ob diese Methoden einen geeigneten Prototyp haben.
Vielleicht erwarten Sie z. B. bool Init(int, int). Sie könnten ein static_cast verwenden, um die exakte Signatur zu prüfen, aber auch dies könnte unnötige Einschränkungen für den Typ bedeuten. Was ist zum Beispiel, wenn eine Klasse stattdessen bool Init(long, long) hat?)
Auf die eine oder andere Weise scheint dieser Aufwand nur notwendig, um die Fehlermeldungen deutlicher zu machen. Ich bezweifle jedoch sehr, dass eine Meldung, die man sonst ohne Konzeptprüfungen erhalten würde (a la "keine geeignete Methode Init zum Aufruf mit T = X, wenn hier verwendet"), so schlecht ist.