Как определить параметры шаблона базовых классов во время компиляции (для ошибок)? - PullRequest
7 голосов
/ 08 января 2012

Я использовал Любопытно повторяющийся шаблон Общий код выглядит так:

template <typename T> void genericFunction(T &);
template <typename T> struct Functionality {
    void genericMethod() {
        genericFunction(*((T *)this)) ;
    }
};

struct Klass : public Functionality<Klass> {};

void main() {
    Klass obj ;
    obj.genericMethod();
}

template <> void genericFunction<Klass>(Klass &obj) {
    //do stuff with Klass &obj here
}

Сегодня я столкнулся с ошибкой, которая стоила мне около 90 минут разочарования, эта ошибка была вызвана использованием неверного параметра шаблона для моего объявления наследования базового класса, что-то вроде этого:

struct Klass : public Functionality<SomeOtherKlass> {}; //SomeOtherKlass wrong!!!

Я бы хотел улучшить свой код, чтобы было обнаружено это несоответствие между производным классом и параметром шаблона базового класса (время выполнения, время компиляции, в любое время :)), возможно ли это вообще ?, спасибо.

Ответы [ 5 ]

3 голосов
/ 08 января 2012

Вы можете утверждать отношение, например, genericMethod() с использованием функций Boost или C ++ 11:

BOOST_STATIC_ASSERT(( boost::is_base_of<Functionality<T>, T>::value ));

... хотя предполагается, что другой класс также не является производным от Functionality<T>.

Альтернативой может быть утверждение отношения во время выполнения в тестовых сборках:

template <typename T> struct Functionality {
#ifdef TEST_BUILD
    virtual ~Functionality() {}
#endif
    void genericMethod() {
#ifdef TEST_BUILD
        assert(dynamic_cast<T*>(this));
#endif
        genericFunction(*((T *)this)) ;
    }
};

Обратите внимание, что тест не будет работать внутри конструкторов и деструкторов

2 голосов
/ 09 января 2012

В C ++ 11 должно работать следующее:

template<typename T> class Base
{
  friend T; // allowed in C++11
private:
  ~Base() {}
public:
  // ...
};

class Derived: public Base<Derived> {}; // OK

class WronglyDerived: public Base<Derived> {}; // Error: destructor of base class is private
1 голос
/ 08 января 2012

Вы можете использовать dynamic_cast, который будет возвращать ноль, если у вас неправильный тип параметра.(Для работы вам понадобится хотя бы одна виртуальная функция в базе - скажем, деструктор.)

Если вы беспокоитесь об эффективности, boost имеет polymorphic_cast, который выполняет динамическое приведение в режиме отладки.но статический бросок для производства.

(И в любом случае было бы неплохо избегать использования броска в стиле C).

0 голосов
/ 09 января 2012

Наиболее реальное на данный момент предложение - использовать dynamic_cast для представления некорректных объявлений наследования в конструкторе базового класса, например:

#include <iostream>
template <typename T> struct Base {
    Base() {
        std::cout<<dynamic_cast<T *> (this)<<std::endl;
    }
    virtual void iampolymorphic(){}
};
struct Decoy {} ;
struct Pass : public Base<Pass>{}; //correct
struct Fail : public Base<Decoy>{}; //incorrect
int main() {
    Pass p ;
    Fail f ;
    return 1 ;
}

Этот код компилируется в g ++ 4.6.1, Amd64 Xubuntu 11.10.Выходными данными для обеих операций динамического приведения является нулевой указатель.Комментарии, критика и замечания приветствуются.

0 голосов
/ 08 января 2012

Предположим, вы добавили в базу шаблонный конструктор, который принимает указатель на произвольный тип;

template<class U> Functionality(U *) { ... }

Тогда конструктор каждого производного класса может передать свой указатель this в конструктор и в теле конструкторавы просто статично утверждаете, что U и T - это один и тот же тип.

Параметр конструктора фактически никогда не используется, поэтому его следует оптимизировать полностью.И если это единственный конструктор базового класса, вы не можете его назвать.Единственная проблема будет, если вы передадите что-то, кроме этого.

...