2010-06-17 11 views
8

Ich frage mich, gibt es eine Möglichkeit, Einschränkungen für die Vorlagenklasse festlegen?
Geben Sie an, dass jeder in der Vorlage ersetzte Typ einen bestimmten Vorgänger haben muss (eine Schnittstelle realisieren).C++ - Vorlage Einschränkungen

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(); 
    } 
}; 

Wie => in Haskell

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

Warum glauben Sie, diese brauchen? – fredoverflow

+0

möglich Duplikat von [C++ Klassenvorlage einer bestimmten Basisklasse] (http://stackoverflow.com/questions/2012950/c-class-template-of-specific-baseclass) – SLaks

+1

sooooo keine Anfängerfrage :-). Konzepte sind eigentlich ziemlich umstritten. –

Antwort

2

Sie können use BOOST_STATIC_ASSERT oder eine ähnliche Bibliothek Ihre Beschränkungen für die Template-Parameter behaupten.

Zum Beispiel:

#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: Für Ihr Beispiel: Sie

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

schreiben Sie einen Trick wie diese verwenden könnten (wenn Sie nicht-Boost verwenden wollen) :

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 
} 

Die erste Zeile wird kompiliert. Die zweite wird nicht kompiliert und die folgenden Fehler geben:

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

Dieser Code schlägt unter g ++ 4.4 aus einem unbekannten Grund nicht fehl.Im Allgemeinen würde ich sagen, dass in diesem Fall normalerweise eine Enumeration verwendet wird: enum {TEMPLATE_REQUIRES_BASE_CLASS = 0}; Dies hat den zusätzlichen Vorteil, wie erwartet unter g ++ zu versagen. – iksemyonov

+0

@Semen, GCC ist nicht konform. Der Standard erfordert, dass die Deklaration des Datenelements instanziiert wird, wenn die Klasse implizit instanziiert wird, und ein In-Class-Initialisierer ist Teil der Deklaration anstelle der Definition derselben. Der Standard ist jedoch etwas unklar und sagt: "Die Initialisierung (und alle damit verbundenen Nebenwirkungen) eines statischen Datenelements tritt nur auf, wenn das statische Datenelement selbst so verwendet wird, dass die Definition des statischen Datenelements erforderlich ist. " - Diese Regel scheint jedoch nur für einen Out-of-Class-Initialisierer zu gelten (macht sonst keinen Sinn) –

+0

@Johannes: danke für die Erklärung, ich dachte, ich würde verrückt, wenn es gut gebaut ... aber ich denke, Enum ist immer noch einfacher zu verwenden. (Ich folge nur Vandervourde :) – iksemyonov

6

Eine zukünftige Version von C++ wird diese nativ mit Konzepten unterstützen (die es nicht in C++ 11).

Eine Möglichkeit, das Problem zu nähern ist die Spezialisierung auf eine Dummy-Template-Parameter zu verwenden:

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> 

I Stand-alone-Definitionen von enable_if und is_base_and_derivedhere gesetzt haben.

+0

Danke, tolles Stück Code – Andrew

1

Vorlagen sind eine Art Ente, die in C++ schreibt.

Wenn Ihre Klasse alles unterstützt, was die Vorlage verwendet, kann sie als Vorlageargument verwendet werden, andernfalls nicht.

Wenn in Ihrer Vorlage haben Sie so etwas wie

C *instance; 

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

dann sind Sie erzwingen, dass T abgeleitet von C (oder zumindest assignement-kompatibel für Zeiger)

3

folgende Arbeiten in VC10 mit static_assert. Ich habe gerade gesehen, dass dies verwendet wird und habe mich nicht wirklich damit beschäftigt, was static_assert tatsächlich tut - vielleicht kann jemand anderes das beantworten.

#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; 
} 

Der Compiler ausgegeben wird:

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>   ]