tags:

views:

340

answers:

6

Hi,

I've a basic template class, but I'd like to restrain the type of the specialisation to a set of classes or types. e.g.:

template <typename T>
class MyClass
{
.../...
private:
    T* _p;
};

MyClass<std::string> a; // OK
MYCLass<short> b;       // OK
MyClass<double> c;      // not OK

Those are just examples, the allowed types may vary.

Is that even possible? If it is, how to do so?

Thanks.

+5  A: 

Take a look at the Boost Concept Check Library: http://www.boost.org/doc/libs/1_42_0/libs/concept_check/concept_check.htm

Axel Gneiting
+1  A: 

There are various tricks allowing to check for some things, depending on what your criteria is for the instantiation be allowed or not. In practice you should use an higher lever library for those like Boost's Concept Check.

AProgrammer
+8  A: 

Yust a quick idea, I'm sure there are better approaches:

template <typename T> struct protector {
static const int result = 1;
};

template <> struct protector<double> {
static const int result = -1;
};

template <typename T> 
class MyClass
{
   private:
     char isfine[protector<T>::result];
};

It might be better, however, to put a fat comment over your code to keep users from instantiating with the wrong types :-)

Alexander Gessler
You beat me to it. The idiom used here is called *type traits*.
Emile Cormier
Is there another way to do this without incurring the cost of a char[1] array in every instance? Personally, I'd use BOOST_STATIC_ASSERT.
Emile Cormier
Add `typedef` before `char`.
Ari
And I'd probably use `enum` instead of `static const int` (i.e., `enum { result = 1 }` and `enum { result = -1; }`. This way I'm guaranteed no space is allocated for this trickery.
Ari
@Ari: That value is only used by the compiler to determine the size of the array. No instance of the class is created or exists at runtime.
UncleBens
I quite like using a switch statement. `void isfine() { switch (0) { case -1: case Protector<T>::result }; }`
Charles Beattie
@Charles: You should be aware that this function is only compiled if it is used somewhere. It takes some more work to make it assert anything, since as it is, it doesn't stop you from instantiating MyClass<double>.
UncleBens
@Alexandre: I would probably use a whitelist instead of a blacklist as it is difficult to guess which types a user is going to create.
Matthieu M.
@Matthieu: Without a concrete example it would be hard to know what would go in the whitelist, either. As far as we know, there might be an infinite number of classes that might be OK, and an infinite number of classes that need to be disabled.
UncleBens
@UncleBens: It's `static`. Ordinarily this means that it is created even if no instance of the class is created. In this case it is also `const` so the compiler is probably free not to create it, but I'm pretty sure there's no guarantee of that.
Ari
+1  A: 

Im not sure about this, but you could add another template specialization for double template

class MyClass
{
.../...
private:
    T* _p;
};

template <double> class MyClass
{};

which would work for your example, but not for the general case.

In general, I would add a compile assert to check for unwanted types.

Hope it helps.

Tom
+2  A: 

Generally it is unnecessary to restrict which types templates can be instantiated with. Either the template is compilable with the given type (and works OK) or it isn't (and produces a compiler error without any effort on the programmer's part).


If you need to put in restrictions, generally the types have something in common that may be described by some type traits that are already available (standard library, boost::type_traits), or you can create a new type trait for them.

For example, here's a template class that only allows integer types, using std::numeric_limits to check it (if you write your own numeric type, you may specialize that so that it would also work with your new integer type). static_assert is C++0x only, if not available use BOOST_STATIC_ASSERT or some other trick.

#include <limits>
#include <string>

template <class T>
class X
{
    static_assert(std::numeric_limits<T>::is_integer, "X can be only instantiated with integer types");
    //...
};

int main()
{
    X<int> xi;
    X<char> xc;
    //X<double> xd;
    //X<std::string> xs;
}

If you only plan to support a handful of arbitrary types with nothing in common (as is apparent from your hypothetical example), one way is to employ typelists. Again boost might make the task a lot easier, but here's how you might roll your own (this only goes half-way, additional work would be required to make declaring the typelist prettier).

struct no_type {};

template <class T, class U = no_type>
struct t_list
{
    typedef T head;
    typedef U tail;
};

//trait to check if two types are identical
template <class T, class U>
struct is_same
{
    static const bool value = false;
};

template <class T>
struct is_same<T, T>
{
    static const bool value = true;
};

//compile-time recursion to check if T matches any type in list L
template <class T, class L>
struct in_type_list
{
    static const bool value =
        is_same<T, typename L::head>::value || in_type_list<T, typename L::tail>::value;
};

//terminates recursion
template <class T>
struct in_type_list<T, no_type>
{
    static const bool value = false;
};

template <class T>
class X
{
    typedef t_list<double, t_list<int, t_list<char> > > allowed_types; //double, int, char

    //poor man's static_assert
    typedef int check_type [in_type_list<T, allowed_types>::value ? 1 : -1];
    //...
};

int main()
{
    X<char> xc;
    X<int> xi;
    X<double> xd;
    //X<float> xf;
}
UncleBens
+4  A: 

Another version is to leave it undefined for the forbidden types

template<typename T>
struct Allowed; // undefined for bad types!

template<> struct Allowed<std::string> { };
template<> struct Allowed<short> { };

template<typename T>
struct MyClass : private Allowed<T> { 
  // ...
};

MyClass<double> m; // nono
Johannes Schaub - litb
+1. In fact, I like this solution better than the first answer.
paercebal
@paercebal, thanks mate xD
Johannes Schaub - litb
@Johannes Schaub - litb: You're welcome. Anyway, since my last comment, your answer became the selected answer, so apparently, I'm not the only one to see its values... :-P
paercebal