9

I wondering is there any way to set restrictions on template class?
Specify that every type substituted in template must have specific ancestor (realize some interface).

template < class B > //and every B must be a child of abstract C class A { public: B * obj; int f() { return B::x + this->obj->f(); } }; 

Like => in haskell

func :: (Ord a, Show b) => a -> b -> c 
3
  • 1
    Why do you think you need this? Commented Jun 17, 2010 at 14:14
  • possible duplicate of C++ class template of specific baseclass Commented Jun 17, 2010 at 14:14
  • 1
    sooooo not a beginner question :-). Concepts are actually pretty controversial. Commented Jun 17, 2010 at 14:33

5 Answers 5

7

A future version of C++ will support this natively using concepts (which didn't make it into C++11).

One way to approach the problem is to use specialisation on a dummy template parameter:

class C {}; template <class B, class dummy=void> class A; template <class B> class A<B, typename enable_if<is_base_and_derived<C, B> >::type> { // class definition here }; struct D : C {}; A<D> d; // fine A<int> n; // compile error - undefined class A<B> 

I've put stand-alone definitions of enable_if and is_base_and_derived here.

Sign up to request clarification or add additional context in comments.

Comments

3

The following works in VC10 using static_assert. I've just seen this used and haven't really dug around much into what static_assert actually does - perhaps someone else can answer that.

#include <type_traits> class Base { }; class Derived : public Base { }; class SomeRandomClass { }; template<typename T> class A { static_assert(std::tr1::is_base_of<Base, T>::value, "T not derived from Base"); }; int _tmain(int argc, _TCHAR* argv[]) { argc; argv; // // This will compile A<Derived> a; // // This will throw a compilation error A<SomeRandomClass> b; return 0; } 

The compiler output being:

1>d:\temp\aaa\aaa\aaa.cpp(25): error C2338: T not derived from Base 1> d:\temp\aaa\aaa\aaa.cpp(41) : see reference to class template instantiation 'A<T>' being compiled 1> with 1> [ 1> T=SomeRandomClass 1> ] 

Comments

2

You can use BOOST_STATIC_ASSERT or a similar library to assert your restrictions on the template parameter.

For example:

#include <limits> #include <boost/static_assert.hpp> template <class UnsignedInt> class myclass { private: BOOST_STATIC_ASSERT((std::numeric_limits<UnsignedInt>::digits >= 16) && std::numeric_limits<UnsignedInt>::is_specialized && std::numeric_limits<UnsignedInt>::is_integer && !std::numeric_limits<UnsignedInt>::is_signed); public: /* details here */ }; 

EDIT: For your example, you can write

template < class B > class A { BOOST_STATIC_ASSERT(boost::is_base_of<C, B>); public: B * obj; int f() { return B::x + this->obj->f(); } }; 

Comments

2

You could use a trick like this (if you don't want to use Boost):

class Base { public: static const int TEMPLATE_REQUIRES_BASE_CLASS = 0; }; class Correct : public Base { }; class Incorrect { }; template <typename T> class TMPL { static const int TEMPLATE_REQUIRES_BASE_CLASS = T::TEMPLATE_REQUIRES_BASE_CLASS; T *m_t; }; void main() { TMPL<Correct> one; // OK TMPL<Incorrect> two; // Will not compile } 

The first line will compile. The second will not compile and will give the following error:

test.cpp test.cpp(18) : error C2039: 'TEMPLATE_REQUIRES_BASE_CLASS' : is not a member of 'Incorrect' test.cpp(12) : see declaration of 'Incorrect' test.cpp(25) : see reference to class template instantiation 'TMPL<T>' being compiled with [ T=Incorrect ] test.cpp(18) : error C2065: 'TEMPLATE_REQUIRES_BASE_CLASS' : undeclared identifier test.cpp(18) : error C2057: expected constant expression 

3 Comments

This code doesn't fail under g++ 4.4, due to some unknown reason. In general, I'd say that in this case, an enum is usually used: enum { TEMPLATE_REQUIRES_BASE_CLASS = 0 }; This has the additional benefit of failing as expected under g++.
@Semen, GCC is non-conforming. The Standard requires the declaration of the data member to be instantiated when the class is implicitly instantiated, and an in-class initializer is part of the declaration instead of the definition of it. However the Standard is slightly unclear, saying "the initialization (and any associated side-effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist." - this rule however seems to apply only to an out-of-class initializer (makes no sense otherwise)
@Johannes: thanks for the explanation, I thought I was going crazy when it built fine... but I guess enum is still simpler to use. (I'm just following Vandervourde :)
1

Templates are sort of duck typing in C++.

If your class supports everything that the template uses, then it can be used as template argument, otherwise it cannot.

If in your template you have something like

C *instance; void foo(T *t) { instance = t; } 

then you're enforcing that T is derived from C (or at least assignement-compatible for pointers)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.