2009-02-24 8 views
19

Je cherche un moyen d'identifier les types de primitives dans une définition de classe de modèle.Identification des types de primitives dans les modèles

Je veux dire, ayant cette classe:

template<class T> 
class A{ 
void doWork(){ 
    if(T isPrimitiveType()) 
    doSomething(); 
    else 
    doSomethingElse(); 
} 
private: 
T *t; 
}; 

est-il est un moyen de "mettre en œuvre" isPrimitiveType().

Répondre

22

MISE À JOUR: Depuis C++ 11, utilisez le modèle is_fundamental de la bibliothèque standard:

#include <type_traits> 

template<class T> 
void test() { 
    if (std::is_fundamental<T>::value) { 
     // ... 
    } else { 
     // ... 
    } 
} 

// Generic: Not primitive 
template<class T> 
bool isPrimitiveType() { 
    return false; 
} 

// Now, you have to create specializations for **all** primitive types 

template<> 
bool isPrimitiveType<int>() { 
    return true; 
} 

// TODO: bool, double, char, .... 

// Usage: 
template<class T> 
void test() { 
    if (isPrimitiveType<T>()) { 
     std::cout << "Primitive" << std::endl; 
    } else { 
     std::cout << "Not primitive" << std::endl; 
    } 
} 

Afin d'économiser les frais généraux d'appel de fonction, utilisez struct:

template<class T> 
struct IsPrimitiveType { 
    enum { VALUE = 0 }; 
}; 

template<> 
struct IsPrimitiveType<int> { 
    enum { VALUE = 1 }; 
}; 

// ... 

template<class T> 
void test() { 
    if (IsPrimitiveType<T>::VALUE) { 
     // ... 
    } else { 
     // ... 
    } 
} 

Comme d'autres l'ont souligné, vous pouvez économiser votre temps Il suffit de mettre cela en œuvre et d'utiliser is_fundamental de la bibliothèque Boost Type Traits, qui semble faire exactement la même chose.

+0

Notez également que l'inverse existe: 'std :: is_class', par ex.https://stackoverflow.com/questions/11287043/is-there-a-way-to-specialize-a-template-to-target-primitives –

1

En supposant que 'Type primitif' signifie les types intégrés, vous pouvez effectuer une série de spécialisations de modèles. Votre code deviendrait:

template<class T> 
struct A{ 
    void doWork(); 
private: 
    T *t; 
}; 

template<> void A<float>::doWork() 
{ 
    doSomething(); 
} 

template<> void A<int>::doWork() 
{ 
    doSomething(); 
} 

// etc. for whatever types you like 

template<class T> void A<T>::doWork() 
{ 
    doSomethingElse(); 
} 
4

L'exemple suivant (. Tout d'abord écrit dans comp.lang.C++ modéré) illustre l'utilisation de la spécialisation partielle pour imprimer les choses différemment selon si oui ou non ils sont intégrés dans les types.

// some template stuff 
//-------------------- 
#include <iostream> 
#include <vector> 
#include <list> 

using namespace std; 

// test for numeric types 
//------------------------- 
template <typename T> struct IsNum { 
    enum { Yes = 0, No = 1 }; 
}; 


template <> struct IsNum <int> { 
    enum { Yes = 1, No = 0 }; 
}; 


template <> struct IsNum <double> { 
    enum { Yes = 1, No = 0 }; 
}; 

// add more IsNum types as required 

// template with specialisation for collections and numeric types 
//--------------------------------------------------------------- 
template <typename T, bool num = false> struct Printer { 
    void Print(const T & t) { 
     typename T::const_iterator it = t.begin(); 
     while(it != t.end()) { 
      cout << *it << " "; 
      ++it; 
     } 
     cout << endl; 
    } 
}; 

template <typename T> struct Printer <T, true> { 
    void Print(const T & t) { 
     cout << t << endl; 
    } 
}; 

// print function instantiates printer depoending on whether or 
// not we are trying to print numeric type 
//------------------------------------------------------------- 
template <class T> void MyPrint(const T & t) { 
    Printer <T, IsNum<T>::Yes> p; 
    p.Print(t); 
} 

// some test types 
//---------------- 
typedef std::vector <int> Vec; 
typedef std::list <int> List; 

// test it all 
//------------ 
int main() { 

    Vec x; 
    x.push_back(1); 
    x.push_back(2); 
    MyPrint(x);  // prints 1 2 

    List y; 
    y.push_back(3); 
    y.push_back(4); 
    MyPrint(y);  // prints 3 4 

    int z = 42; 
    MyPrint(z);  // prints 42 

    return 0; 
} 
+0

cela a aidé, merci! – Ben

2

Cela ne peut pas être fait exactement comme vous le demandiez. Voici comment cela peut se faire:

template<class T> 
class A{ 
void doWork(){ 
    bool isPrimitive = boost::is_fundamental<T>::value; 
    if(isPrimitive) 
    doSomething(); 
    else 
    doSomethingElse(); 
} 
private: 
T *t; 
}; 

Vous pouvez obtenir un avertissement si vous mettez directement la valeur de IsPrimitive dans l'instruction si. C'est pourquoi j'ai introduit une variable temporaire.

2

Encore une autre exemples similaires:

#include <boost/type_traits/is_fundamental.hpp> 
#include <iostream> 

template<typename T, bool=true> 
struct foo_impl 
{ 
    void do_work() 
    { 
     std::cout << "0" << std::endl; 
    } 
}; 
template<typename T> 
struct foo_impl<T,false> 
{ 
    void do_work() 
    { 
     std::cout << "1" << std::endl; 
    } 
}; 

template<class T> 
struct foo 
{ 
    void do_work() 
    { 
     foo_impl<T, boost::is_fundamental<T>::value>().do_work(); 
    } 
}; 


int main() 
{ 
    foo<int> a; a.do_work(); 
    foo<std::string> b; b.do_work(); 
} 
+0

Votre version est un peu plus efficace que la mienne, mais j'ai considéré qu'elle ne compensait pas le coût de la lisibilité. –

+0

Probablement il y a encore des façons plus agréables de le faire. Ce qui précède a été rapidement piraté à vim pour tester l'idée ... – Anonymous

5

Je suppose que cela peut faire le travail tout à fait bien, sans de multiples spécialisations:

# include <iostream> 
# include <type_traits> 

template <class T> 
inline bool isPrimitiveType(const T& data) { 
    return std::is_fundamental<T>::value; 
} 

struct Foo { 
    int x; 
    char y; 
    unsigned long long z; 
}; 


int main() { 

    Foo data; 

    std::cout << "isPrimitiveType(Foo): " << std::boolalpha 
     << isPrimitiveType(data) << std::endl; 
    std::cout << "isPrimitiveType(int): " << std::boolalpha 
     << isPrimitiveType(data.x) << std::endl; 
    std::cout << "isPrimitiveType(char): " << std::boolalpha 
     << isPrimitiveType(data.y) << std::endl; 
    std::cout << "isPrimitiveType(unsigned long long): " << std::boolalpha 
     << isPrimitiveType(data.z) << std::endl; 

} 

et la sortie est:

isPrimitiveType(Foo): false 
isPrimitiveType(int): true 
isPrimitiveType(char): true 
isPrimitiveType(unsigned long long): true 
3

Il est un meilleur moyen - en utilisant SFINAE. Avec SFINAE vous n'avez pas besoin d'énumérer tous les types primitifs. SFINAE est une technique qui dépend de l'idée que lorsque la spécialisation de modèle échoue, elle revient à un modèle plus général. (Cela signifie "L'échec de la spécialisation n'est pas une erreur").

De même, vous ne définissez pas vraiment si vous considérez qu'un pointeur est un type primitif, je vais donc créer des modèles pour toutes les combinaisons.

// takes a pointer type and returns the base type for the pointer. 
// Non-pointer types evaluate to void. 
template < typename T > struct DePtr      { typedef void R; }; 
template < typename T > struct DePtr< T * >    { typedef T R; }; 
template < typename T > struct DePtr< T * const >   { typedef T R; }; 
template < typename T > struct DePtr< T * volatile >  { typedef T R; }; 
template < typename T > struct DePtr< T * const volatile > { typedef T R; }; 

// ::value == true if T is a pointer type 
template < class T > struct IsPointer      { enum { value = false }; }; 
template < class T > struct IsPointer < T *    > { enum { value = true }; }; 
template < class T > struct IsPointer < T * const   > { enum { value = true }; }; 
template < class T > struct IsPointer < T * volatile  > { enum { value = true }; }; 
template < class T > struct IsPointer < T * const volatile > { enum { value = true }; }; 

// ::value == true if T is a class type. (class pointer == false) 
template < class T > struct IsClass 
{ 
    typedef u8 yes; typedef u16 no; 
    template < class C > static yes isClass(int C::*); 
    template < typename C > static no isClass(...); 
    enum { value = sizeof(isClass<T>(0)) == sizeof(yes) }; 
}; 

// ::value == true if T* is a class type. (class == false) 
template < class T > struct IsClassPtr 
{ 
    typedef u8 yes; typedef u16 no; 
    template < class C > static yes isClass(int C::*); 
    template < typename C > static no isClass(...); 
    enum { value = sizeof(isClass< typename DePtr<T>::R >(0)) == sizeof(yes) }; 
}; 

// ::value == true if T is a class or any pointer type - including class and non-class pointers. 
template < class T > struct IsClassOrPtr : public IsClass<T> { }; 
template < class T > struct IsClassOrPtr < T *    > { enum { value = true }; }; 
template < class T > struct IsClassOrPtr < T * const   > { enum { value = true }; }; 
template < class T > struct IsClassOrPtr < T * volatile  > { enum { value = true }; }; 
template < class T > struct IsClassOrPtr < T * const volatile > { enum { value = true }; }; 


template < class T > struct IsClassOrClassPtr : public IsClass<T> { }; 
template < class T > struct IsClassOrClassPtr < T *    > : public IsClassPtr< T*    > { }; 
template < class T > struct IsClassOrClassPtr < T * const   > : public IsClassPtr< T* const   > { }; 
template < class T > struct IsClassOrClassPtr < T * volatile  > : public IsClassPtr< T* volatile  > { }; 
template < class T > struct IsClassOrClassPtr < T * const volatile > : public IsClassPtr< T* const volatile > { }; 
Questions connexes