Setting allowed arguments in templates

Is it possible to specify exactly what arguments a template can receive? For example, I would like to create a template that can only be created using classes that are or extend class A In Java, shared files support this:

 class B<T extends A> { } 

Is it possible to achieve something like this using templates in C ++?

 template <typename T (?)> class B { } 
+4
source share
2 answers

There are two ways to do this.

Firstly, using the parameter of the hidden template template that uses std::enable_if with the condition std::is_base_of<A, T>::value as. If the last expression is false , then the nested type does not exist in std::enable_if . If you used this with overloaded functions, SFINAE means that “replacement failure is not an error,” and the overload in question will be removed from the set of viable functions. Although in this situation there is no other class template to match your call, and then you will get a compile time error.

SFINAE is a very delicate mechanism and is easily mistaken. For instance. if you have several class specializations with different SFINAE conditions, you must make sure that they all do not overlap, or you get ambiguity.

Secondly, you can make simple static_assert with std::is_base_of<A,T>::value inside the class body. The advantage of this method is that you also provide a more readable error message than the SFINAE method. The downside is that you always get an error message, and you cannot silently disable this particular template and choose another one. But overall, I think this method is recommended in your case.

 #include<type_traits> class A {}; class C: public A {}; class D {}; // first alternative: SFINAE on hidden template parameter template < typename T, typename /* dummy */ = typename std::enable_if< std::is_base_of<A, T>::value >::type > class B { }; // second alternative: static_assert inside class template < typename T > class E { static_assert(std::is_base_of<A, T>::value, "A should be a base of T"); }; int main() { B<A> b1; B<C> c1; //B<D> d1; // uncomment this line to get a compile-time error E<A> b2; E<C> c2; //E<D> d2; // uncomment this line to get a compile-time error return 0; } 

As noted in the comments, you can use either the decent C ++ 11 compiler (VC ++ 2010 or later, gcc 4.5 or later), or the Boost or TR1 libraries to get <type_traits> functionality. Note, however, that std::is_base_of<A, A>::value evaluates to true , but the old boost::is_base_of<A, A>::value used to check for false .

+3
source

You can do this with static_assert and is_base_of :

 #include <type_traits> template<typename T> class D { static_assert(std::is_base_of<A, T>::value, "must be derived from A"); }; 

Or you can use enable_if :

 #include <type_traits> template<typename T, typename = void> class D; template<typename T> class D<T, typename std::enable_if<std::is_base_of<A, T>::value>::type> { }; 

For C ++ 03 you can use boost; is_base_of from Boost.TypeTraits , static_assert of Boost.StaticAssert , enable_if of Boost.EnableIf .

+3
source

All Articles