2010-01-06 13 views
38

Diciamo che ho le classi:C++ di baseclass specifici

class Base{}; 

class A: public Base{ 
    int i; 
}; 

class B:public Base{ 
    bool b; 
}; 

e ora voglio definire una classe template:

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

ma voglio definirla tale che solo discendenti di classe Base può essere usato come templateparameters.

Come posso farlo?

+3

È necessario correggere la sintassi del modello. Intendi A e B come derivati ​​privati? –

risposta

12

Più esattamente:

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++ non lo consente ancora direttamente. È possibile realizzarlo indirettamente utilizzando un STATIC_ASSERT e type checking all'interno della classe:

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

Non voglio che i parametri abbiano la stessa classe base - voglio che abbiano la mia specifica classe Base come base di partenza – Mat

+0

@ Math - oops, controllo di tipo errato. Ero ancora stanco –

+3

La parola chiave è "ancora". Per il beneficio di altri che arrivano qui da un motore di ricerca, 'static_assert' e' is_base_of' si sono laureati in lingue ufficiali in C++ 11. Rappresentano un codice molto più intuitivo/meno terrificante della risposta accettata. –

0

In primo luogo, fissare la dichiarazione

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

Quindi, è possibile dichiarare in una classe base qualche funzione privata Foo(); e dì alla classe Base di avere BasePair come amico; quindi nel costruttore di amici devi solo chiamare questa funzione. In questo modo si otterrà un errore in fase di compilazione quando qualcuno tenta di utilizzare altre classi come parametri del modello.

+3

"correggi la dichiarazione" - lo stesso vale per te. ;-) –

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

spiega per favore .. come fa questo garantisce che i parametri del template siano i discendenti di B? – Mat

+0

Quando si crea un'istanza del modello P ok; il compilatore proverà a utilizzare la funzione template static_cast per convertire il puntatore di tipo D in puntatore di tipo B. Questo funzionerà bene ma, in caso di P nok; questa conversione fallisce e riceverai un errore del compilatore (errore C2440: 'static_cast': impossibile convertire da 'U *' a 'B *'). Inoltre, questo non avrà alcun effetto di runtime (il codice non è più lento). È un controllo di tipo statico o in fase di compilazione. – user213546

2

Questa è stata una grande domanda! Durante la ricerca di questo tramite questo link, ho trovato il seguente, che non è molto diverso dalla soluzione fornita lì. Impara qualcosa ogni giorno ... controlla!

#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

Nella risposta suggerita da unknown (yahoo), non è necessario avere effettivamente le variabili di tipo X e Y come membri. Queste linee sono sufficienti nel costruttore:

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

C++ 11 introduce <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; 
}; 
Problemi correlati