Как избежать ошибок при использовании CRTP?
Используя CRTP иногда я пишу код, подобный этому:
// this was written first
struct Foo : Base<Foo, ...>
{
...
};
// this was copy-pasted from Foo some days later
struct Bar : Base<Foo, ...>
{
...
};
И очень трудно понять, что идет не так, пока я не отследил код в отладчике и не увидел, что члены Bar не используются в Base
,
Как выявить эту ошибку во время компиляции?
(Я использую MSVC2010, поэтому я могу использовать некоторые функции C++0x и расширения языка MSVC)
5 ответов
В C++0x у вас есть простое решение. Однако я не знаю, реализовано ли это в MSVC10.
template <typename T>
struct base
{
private:
~base() {}
friend T;
};
// Doesn't compile (base class destructor is private)
struct foo : base<bar> { ... };
Вы можете использовать что-то вроде этого:
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) { }
};
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
};
Этот код основан на ответе Амнона, но проверка кода не содержит имени производного класса, поэтому я могу скопировать и вставить его без изменений.
Там нет никакого способа узнать производный тип. Вы могли бы обеспечить это Foo
происходит от Base<Foo>
, но вы не можете принудительно установить, что другие классы также не являются производными от этого.
Я могу использовать макрос
#define SOMENAMESPACE_BASE(type, arg1, arg2) type : Base<type, arg1, arg2>
но я не хочу использовать макросы, если существует лучшее решение.