views:

2277

answers:

9

In C# we can define a generic type that imposes constraints on the types that can be used as the generic parameter. The following example illustrates the usage of generic constraints:

interface IFoo
{
}


class Foo<T> where T : IFoo
{
}

class Bar : IFoo
{
}

class Simpson
{
}

class Program
{
    static void Main(string[] args)
    {
        Foo<Bar> a = new Foo<Bar>();
        Foo<Simpson> b = new Foo<Simpson>(); // error CS0309
    }
}

Is there a way we can impose constraints for template parameters in C++.


C++0x has native support for this but I am talking about current standard C++.

+1  A: 

Only implicitly.
Any method you use in a method that is actually called is imposed on the template parameter.

shoosh
+1  A: 

Sort of. If you static_cast to an IFoo*, then it will be impossible to instantiate the template unless the caller passes a class that can be assigned to an IFoo *.

Lou Franco
+15  A: 

As someone else has mentioned, C++0x is getting this built into the language. Until then, I'd recommend Bjarne Stroustrup's suggestions for template constraints.

Edit: Boost also has an alternative of it's own.

Editx2: Looks like concepts have been removed from C++0x.

luke
+2  A: 

"Implicitly" is the correct answer. Templates effectively create a "duck typing" scenario, due to the way in which they are compiled. You can call any functions you want upon a template-typed value, and the only instantiations that will be accepted are those for which that method is defined. For example:

template <class T>
int compute_length(T *value)
{
    return value->length();
}

We can call this method on a pointer to any type which declares the length() method to return an int. Thusly:

string s = "test";
vector<int> vec;
int i = 0;

compute_length(&s);
compute_length(&vec);

...but not on a pointer to a type which does not declare length():

compute_length(&i);

This third example will not compile.

This works because C++ compiles a new version of the templatized function (or class) for each instantiation. As it performs that compilation, it makes a direct, almost macro-like substitution of the template instantiation into the code prior to type-checking. If everything still works with that template, then compilation proceeds and we eventually arrive at a result. If anything fails (like int* not declaring length()), then we get the dreaded six page template compile-time error.

Daniel Spiewak
+5  A: 

You can put a guard type on IFoo that does nothing, make sure it's there on T in Foo:

class IFoo
{
public:
    typedef int IsDerivedFromIFoo;
};

template <typename T>
class Foo<T>
{
    typedef typename T::IsDerivedFromIFoo IFooGuard;
}
Eclipse
+2  A: 

I found the following to be a very interesting read on the topic:

que que
A: 

You can do it. Create the base template. Make it have only Private constructors. Then create specializations for each case you want to allow (or make the opposite if the disallowed list is much smaller than the allowed list).

The compiler will not allow you to instantiate the templates that use the version with private constructors.

This example only allow instantiation with int and float.

template class FOO { private: FOO(){}};

template<> class FOO{public: FOO(){}};

template<> class FOO{public: FOO(){}};

Its not a short and elegant way of doing it, but its possible.

OldMan
A: 

Look at the CRTP pattern (Curiously Recursive Template Pattern). It is designed to help support static inheritence.