Reputation: 97967
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++.
Upvotes: 89
Views: 72879
Reputation: 414
Using C++20, yes there is: Constraints and concepts
Perhaps you want to guarantee a template is derived from a specific class:
#include <concepts>
template<class T, class U>
concept Derived = std::is_base_of<U, T>::value;
class ABase { };
class ADerived : ABase { };
template<Derived<ABase> T>
class AClass {
T aMemberDerivedFromABase;
};
The following then compiles like normal:
int main () {
AClass<ADerived> aClass;
return 0;
}
But now when you do something against the contraint:
class AnotherClass {
};
int main () {
AClass<AnotherClass> aClass;
return 0;
}
AnotherClass is not derived from ABase, therefore my compiler (GCC) gives roughly the following error:
In function 'int main()': note: constraints not satisfied note: the expression 'std::is_base_of<U, T>::value [with U = ABase; T = AnotherClass]' evaluated to 'false' 9 | concept Derived = std::is_base_of<U, T>::value;
As you can imagine this feature is very useful and can do much more than constraining a class to have a specific base.
Upvotes: 11
Reputation: 19107
If you use C++11, you can use static_assert
with std::is_base_of
for this purpose.
For example,
#include <type_traits>
template<typename T>
class YourClass {
YourClass() {
// Compile-time check
static_assert(std::is_base_of<BaseClass, T>::value, "type parameter of this class must derive from BaseClass");
// ...
}
}
Upvotes: 88
Reputation: 37493
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 its own.
Edit2: Looks like concepts have been removed from C++0x.
Upvotes: 37
Reputation: 24559
Check out Boost
The Boost Concept Check Library (BCCL)
The Concept Check library allows one to add explicit statement and checking of concepts in the style of the proposed C++ language extension.
Upvotes: 9
Reputation:
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 t> class FOO { private: FOO(){}};
template<> class FOO<int>{public: FOO(){}};
template<> class FOO<float>{public: FOO(){}};
Its not a short and elegant way of doing it, but its possible.
Upvotes: 2
Reputation:
Look at the CRTP pattern (Curiously Recursive Template Pattern). It is designed to help support static inheritence.
Upvotes: 0
Reputation: 45533
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;
}
Upvotes: 18
Reputation: 55123
"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.
Upvotes: 47
Reputation: 89232
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 *.
Upvotes: 2
Reputation: 79021
Only implicitly.
Any method you use in a method that is actually called is imposed on the template parameter.
Upvotes: 1