2010-06-17 15 views
8

Mi chiedo se esiste un modo per impostare restrizioni sulla classe template.
Specificare che ogni tipo sostituito nel modello deve avere un antenato specifico (realizzare un'interfaccia).Limitazioni modello C++

template < class B > //and every B must be a child of abstract C 
class A { 
public: 
    B * obj; 
    int f() { 
     return B::x + this->obj->f(); 
    } 
}; 

Come => in Haskell

func :: (Ord a, Show b) => a -> b -> c 
+1

Perché pensi che hai bisogno di questo? – fredoverflow

+0

possibile duplicato di [modello di classe C++ di specifico baseclass] (http://stackoverflow.com/questions/2012950/c-class-template-of-specific-baseclass) – SLaks

+1

non è una domanda per principianti :-). I concetti sono in realtà piuttosto controversi. –

risposta

2

È possibile use BOOST_STATIC_ASSERT o una libreria simile per far valere i restrizioni sul parametro di template.

Ad esempio:

#include <limits> 
#include <boost/static_assert.hpp> 

template <class UnsignedInt> 
class myclass 
{ 
private: 
    BOOST_STATIC_ASSERT((std::numeric_limits<UnsignedInt>::digits >= 16) 
         && std::numeric_limits<UnsignedInt>::is_specialized 
         && std::numeric_limits<UnsignedInt>::is_integer 
         && !std::numeric_limits<UnsignedInt>::is_signed); 
public: 
    /* details here */ 
}; 

EDIT: Per il vostro esempio, è possibile scrivere

template < class B > 
class A { 
    BOOST_STATIC_ASSERT(boost::is_base_of<C, B>); 

public: 
    B * obj; 
    int f() { 
     return B::x + this->obj->f(); 
    } 
}; 
2

si potrebbe usare un trucco del genere (se non si desidera utilizzare Boost) :

class Base 
    { 
    public: 
     static const int TEMPLATE_REQUIRES_BASE_CLASS = 0; 
    }; 

class Correct : public Base 
    { 
    }; 

class Incorrect 
    { 
    }; 

template <typename T> 
class TMPL 
    { 
    static const int TEMPLATE_REQUIRES_BASE_CLASS = T::TEMPLATE_REQUIRES_BASE_CLASS; 
    T *m_t; 
    }; 

void main() 
{ 
TMPL<Correct> one;  // OK 
TMPL<Incorrect> two; // Will not compile 
} 

La prima riga verrà compilata. La seconda non compilerà e darà il seguente errore:

test.cpp 
test.cpp(18) : error C2039: 'TEMPLATE_REQUIRES_BASE_CLASS' : is not a member of 'Incorrect' 
     test.cpp(12) : see declaration of 'Incorrect' 
     test.cpp(25) : see reference to class template instantiation 'TMPL<T>' being compiled 
     with 
     [ 
      T=Incorrect 
     ] 
test.cpp(18) : error C2065: 'TEMPLATE_REQUIRES_BASE_CLASS' : undeclared identifier 
test.cpp(18) : error C2057: expected constant expression 
+0

Questo codice non ha esito negativo in g ++ 4.4, a causa di qualche motivo sconosciuto.In generale, direi che in questo caso viene solitamente utilizzata una enumerazione: enum {TEMPLATE_REQUIRES_BASE_CLASS = 0}; Questo ha l'ulteriore vantaggio di non riuscire come previsto in g ++. – iksemyonov

+0

@Semen, GCC non è conforme. Lo standard richiede che la dichiarazione del membro dati sia istanziata quando la classe viene istanziata in modo implicito e un inizializzatore in classe fa parte della dichiarazione invece della definizione di esso. Tuttavia, lo standard è leggermente poco chiaro, dicendo che "l'inizializzazione (e tutti gli effetti collaterali associati) di un membro di dati statici non si verifica a meno che il membro di dati statici non sia utilizzato in un modo che richiede la definizione del membro di dati statici. " - questa regola sembra però applicarsi solo a un inizializzatore fuori dalla classe (non ha senso altrimenti) –

+0

@Johannes: grazie per la spiegazione, ho pensato che stavo impazzendo quando è stato costruito bene ... ma immagino che enum sia ancora più semplice da usare. (Sto solo seguendo Vandervourde :) – iksemyonov

6

Una futura versione di C++ supporterà in modo nativo utilizzando concetti (che non ha fatto in C++ 11).

Un modo per affrontare il problema è quello di utilizzare la specializzazione su un parametro di modello fittizio:

class C {}; 
template <class B, class dummy=void> 
class A; 

template <class B> 
class A<B, typename enable_if<is_base_and_derived<C, B> >::type> 
{ 
    // class definition here 
}; 

struct D : C {}; 

A<D> d;  // fine 
A<int> n; // compile error - undefined class A<B> 

ho messo le definizioni stand-alone di enable_if e is_base_and_derivedhere.

+0

Grazie, fantastico pezzo di codice – Andrew

1

I modelli sono una sorta di anatra che digita in C++.

Se la classe supporta tutto ciò che utilizza il modello, può essere utilizzato come argomento modello, altrimenti non può.

Se nel modello di avere qualcosa di simile

C *instance; 

void foo(T *t) 
{ 
    instance = t; 
} 

allora stai far rispettare che T è derivato da C (o almeno assegnazione compatibile per i puntatori)

3

le seguenti opere in VC10 utilizzando static_assert. Ho appena visto questo usato e non ho davvero scavato molto in quello che static_assert fa in realtà - forse qualcun altro può rispondere a questo.

#include <type_traits> 

class Base 
{ 

}; 

class Derived : public Base 
{ 

}; 

class SomeRandomClass 
{ 

}; 

template<typename T> 
class A 
{ 
    static_assert(std::tr1::is_base_of<Base, T>::value, "T not derived from Base"); 
}; 



int _tmain(int argc, _TCHAR* argv[]) 
{ 
    argc; argv; 

    // 
    // This will compile 
    A<Derived> a; 

    // 
    // This will throw a compilation error 
    A<SomeRandomClass> b; 

    return 0; 
} 

L'essere output del compilatore:

1>d:\temp\aaa\aaa\aaa.cpp(25): error C2338: T not derived from Base 
1>   d:\temp\aaa\aaa\aaa.cpp(41) : see reference to class template instantiation 'A<T>' being compiled 
1>   with 
1>   [ 
1>    T=SomeRandomClass 
1>   ]