2010-01-06 3 views
38

Скажем, у меня есть классы:C++ шаблон класса специфических BaseClass

class Base{}; 

class A: public Base{ 
    int i; 
}; 

class B:public Base{ 
    bool b; 
}; 

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

template < typename T1, typename T2 > 
class BasePair{ 
    T1 first; 
    T2 second; 
}; 

Но я хочу, чтобы определить его таким образом, что только decendants класса Base можно использовать в качестве шаблона шаблонов.

Как я могу это сделать?

+3

Вы должны исправить синтаксис шаблона. И вы имеете в виду, что A и B должны быть получены в частном порядке? – 2010-01-06 12:50:05

ответ

12

Более точно:

class B {}; 
class D1 : public B {}; 
class D2 : public B {}; 
class U {}; 

template <class X, class Y> class P { 
    X x; 
    Y y; 
public: 
    P() { 
     (void)static_cast<B*>((X*)0); 
     (void)static_cast<B*>((Y*)0); 
    } 
}; 

int main() { 
    P<D1, D2> ok; 
    P<U, U> nok; //error 
} 
8

C++ еще не разрешает это напрямую. Вы можете реализовать это косвенно, используя STATIC_ASSERT и type checking внутри класса:

template < typename T1, typename T2 > 
class BasePair{ 
    BOOST_STATIC_ASSERT(boost::is_base_of<Base, T1>); 
    BOOST_STATIC_ASSERT(boost::is_base_of<Base, T2>); 
    T1 first; 
    T2 second; 
}; 
+0

Я не хочу, чтобы параметры имели только тот же базовый класс - я хочу, чтобы у них был мой базовый класс Base как базовый класс – Mat

+0

@Math - oops, неправильная проверка типа. Все еще устал. –

+3

Ключевое слово «еще». В интересах других, прибывающих сюда из поисковой системы, 'static_assert' и' is_base_of' закончили официальные языковые функции в C++ 11. Они делают гораздо более интуитивный/менее ужасающий код, чем принятый ответ. –

0

Во-первых, исправить декларацию

template < class T1, class T2 > 
class BasePair{ 
    T1 first; 
    T2 second; 
}; 

Затем вы можете объявить в базовом классе некоторые частные функции Foo(); и скажите базовому классу, что BasePair является другом; то в конструкторе друзей вам просто нужно вызвать эту функцию. Таким образом, вы получите ошибку времени компиляции, когда кто-то пытается использовать другие классы в качестве параметров шаблона.

+3

«Исправить объявление» - то же самое касается и вас. ;-) –

0
class B 
{ 
}; 
class D : public B 
{ 
}; 
class U 
{ 
}; 

template <class X, class Y> class P 
{ 
    X x; 
    Y y; 
public: 
    P() 
    { 
     (void)static_cast<X*>((Y*)0); 
    } 
}; 
+0

объясните пожалуйста .. как это гарантирует, что параметры шаблона являются декандантами B? – Mat

+0

При создании экземпляра шаблона P ok; компилятор попытается использовать функцию шаблона static_cast для преобразования указателя типа D в указатель типа B. Это будет работать нормально, но в случае P nok; это преобразование завершается с ошибкой, и вы получите ошибку компилятора (ошибка C2440: «static_cast»: невозможно преобразовать из «U *» в «B *»). Более того, это не будет иметь никакого эффекта во время выполнения (код не медленнее). Это проверка типа статического или компилируемого времени. – user213546

2

Это был большой вопрос! Изучая это с помощью этого link, я придумал следующее, что, по общему признанию, не сильно отличается от предлагаемого решения. Узнайте что-нибудь каждый день ... проверьте!

#include <iostream> 
#include <string> 
#include <boost/static_assert.hpp> 

using namespace std; 

template<typename D, typename B> 
class IsDerivedFrom 
{ 
    class No { }; 
    class Yes { No no[3]; }; 

    static Yes Test(B*); // declared, but not defined 
    static No Test(...); // declared, but not defined 

public: 
    enum { IsDerived = sizeof(Test(static_cast<D*>(0))) == sizeof(Yes) }; 
}; 


class Base 
{ 
public: 
    virtual ~Base() {}; 
}; 

class A : public Base 
{ 
    int i; 
}; 

class B : public Base 
{ 
    bool b; 
}; 

class C 
{ 
    string z; 
}; 


template <class T1, class T2> 
class BasePair 
{ 
public: 
    BasePair(T1 first, T2 second) 
     :m_first(first), 
     m_second(second) 
    { 
     typedef IsDerivedFrom<T1, Base> testFirst; 
     typedef IsDerivedFrom<T2, Base> testSecond; 

     // Compile time check do... 
     BOOST_STATIC_ASSERT(testFirst::IsDerived == true); 
     BOOST_STATIC_ASSERT(testSecond::IsDerived == true); 

     // For runtime check do.. 
     if (!testFirst::IsDerived) 
      cout << "\tFirst is NOT Derived!\n"; 
     if (!testSecond::IsDerived) 
      cout << "\tSecond is NOT derived!\n"; 

    } 

private: 
    T1 m_first; 
    T2 m_second; 
}; 


int main(int argc, char *argv[]) 
{ 
    A a; 
    B b; 
    C c; 

    cout << "Creating GOOD pair\n"; 
    BasePair<A, B> good(a, b); 

    cout << "Creating BAD pair\n"; 
    BasePair<C, B> bad(c, b); 
    return 1; 
} 
0

В ответе, предложенном неизвестным (yahoo), не обязательно иметь переменные типа X и Y в качестве членов. Эти линии являются достаточными в конструкторе:

static_cast<B*>((X*)0); 
static_cast<B*>((Y*)0); 
32

C++ 11 вводит <type_traits>

template <typename T1, typename T2> 
class BasePair{ 
static_assert(std::is_base_of<Base, T1>::value, "T1 must derive from Base"); 
static_assert(std::is_base_of<Base, T2>::value, "T2 must derive from Base"); 

    T1 first; 
    T2 second; 
}; 
Смежные вопросы