By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
446,260 Members | 1,381 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 446,260 IT Pros & Developers. It's quick & easy.

limiting a template class to certain types ?

P: n/a
Is it possible to limit a template class to certain types only. I found
a few things on the net but nothing seems to apply at compile time.

template <typename T>
class AClass
{
public:
AClass() {}
};

AClass<floatfloatSomethng; // yes it's fine
AClass<intintSomething; // no we don't want to allow the creation of
that object

Thank you, -mark

Jul 11 '06 #1
Share this Question
Share on Google+
10 Replies


P: n/a
ma*****@yahoo.com wrote:
Is it possible to limit a template class to certain types only. I found
a few things on the net but nothing seems to apply at compile time.

template <typename T>
class AClass
{
public:
AClass() {}
};

AClass<floatfloatSomethng; // yes it's fine
AClass<intintSomething; // no we don't want to allow the creation of
that object
template<typename T>
class AClass {
public:
AClass() { }
};

template<class AClass<int>;

int main()
{
AClass<doublead;
AClass<intai; // error
}

Jul 11 '06 #2

P: n/a
ma*****@yahoo.com wrote:
Is it possible to limit a template class to certain types only. I
found a few things on the net but nothing seems to apply at compile
time.

template <typename T>
class AClass
{
public:
AClass() {}
};

AClass<floatfloatSomethng; // yes it's fine
AClass<intintSomething; // no we don't want to allow the creation of
that object
The simplest way is probably hide the implementation of your template
type in a separate translation unit and use explicit instantiations
to stuff the same TU with the implementations of the class for some
specific types. A better way would be to design it so there is no
need to limit it.

Or you could do something like this (AKA "type traits"):

template<class Tclass allowed { enum { yes }; };

// then specialise it for the types that you allow:

template<class allowed<float{ public: enum { yes = 1 }; };
template<class allowed<double{ public: enum { yes = 1 }; };

// and last, use it:

template<typename T>
class AClass
{
static const bool allowed = allowed<T>::yes;

public:
AClass() {}
};

int main() {
AClass<floataf;
AClass<intaf; // error
}

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Jul 11 '06 #3

P: n/a
ma*****@yahoo.com posted:

AClass<floatfloatSomethng; // yes it's fine
AClass<intintSomething; // no we don't want to allow the creation of

The examples on this page may interest you:

http://www.boost.org/doc/html/boost_staticassert.html

(However, one of the examples is dubious in that it should use
"numeric_limits<T>::digits" rather than "sizeof(T) * CHAR_BIT" ).

--

Frederick Gotham
Jul 11 '06 #4

P: n/a
Thanks everyone for your help !

Jul 12 '06 #5

P: n/a
Victor Bazarov wrote:
Or you could do something like this (AKA "type traits"):

template<class Tclass allowed { enum { yes }; };

// then specialise it for the types that you allow:

template<class allowed<float{ public: enum { yes = 1 }; };
template<class allowed<double{ public: enum { yes = 1 }; };

// and last, use it:

template<typename T>
class AClass
{
static const bool allowed = allowed<T>::yes;

public:
AClass() {}
};

int main() {
AClass<floataf;
AClass<intaf; // error
}
This works great for allowing certain templated classes to be used.
But what about functions and overloaded operators? For instance, if I
have a certain family of template classes, how could I overload an
operator so that it only acts on these classes? I'm thinking in
particular of boost::lambda, where they overload just about every
operator to work on the placeholders, but boost::spirit and many other
libraries do this sort of thing as well, even without the proposed
(TR2?) "concept" formalism.

To be more concrete. Suppose I have a predicate template of some sort,
either like your allowed<T>::yes enum, or maybe a bool
allowed_p<T>::value. Short of sticking all the allowed classes into an
unnamed namespace to restrict scope (since this allows classes from
global scope as well), how could I write this function

template<class T>
increment_return_type<T>::type operator++(T x);

so that T can ONLY be a class which satisfies the predicate?

Thanks,
steve hicks

Jul 12 '06 #6

P: n/a
Steve Hicks wrote:
[..] Suppose I have a predicate template of some
sort, either like your allowed<T>::yes enum, or maybe a bool
allowed_p<T>::value. Short of sticking all the allowed classes into
an unnamed namespace to restrict scope (since this allows classes from
global scope as well), how could I write this function

template<class T>
increment_return_type<T>::type operator++(T x);

so that T can ONLY be a class which satisfies the predicate?
I probably don't understand the requirements. Could you please provide
a C++ illustration (even if it doesn't compile)?

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Jul 12 '06 #7

P: n/a
To be more concrete. Suppose I have a predicate template of some sort,
either like your allowed<T>::yes enum, or maybe a bool
allowed_p<T>::value. Short of sticking all the allowed classes into an
unnamed namespace to restrict scope (since this allows classes from
global scope as well), how could I write this function

template<class T>
increment_return_type<T>::type operator++(T x);

so that T can ONLY be a class which satisfies the predicate?
Just add a test, using the typedef T type; trick again.

template<bool bstruct test; // undefined
template<struct test<true{ }; // defined specialization
template<typename T, bool b>
struct test_identity<T: test<b{
typedef T type;
};

template<class T>
increment_return_type<test_identity<T,
allowed_p<T>::value>::type>::type
operator++(T x);

HTH,
Michiel Salters

Jul 12 '06 #8

P: n/a
Mi*************@tomtom.com wrote:
Just add a test, using the typedef T type; trick again.

template<bool bstruct test; // undefined
template<struct test<true{ }; // defined specialization
template<typename T, bool b>
struct test_identity<T: test<b{
typedef T type;
};

template<class T>
increment_return_type<test_identity<T,
allowed_p<T>::value>::type>::type
operator++(T x);
That's a neat trick. Unfortunately, it doesn't quite do what I need.
Here is a small example:

/* BEGIN CODE */
#include <iostream>
template <bool bstruct test;
template <struct test<true{ };
template <class T, bool b>
struct test_identity : test<b{
typedef T type;
};
template <class Tstruct is_callable {
static const bool value = false;
};

template <class Tclass Constant {
T value;
public:
Constant(T t) : value(t) { }
void operator()()
{ std::cout << "value: " << value << std::endl; }
};
template <class Tstruct is_callable<Constant<T {
static const bool value = true;
};

// T1 and T2 must be callable
template <class T1, class T2class Sum {
T1 t1; T2 t2;
public:
Sum(T1 _t1, T2 _t2) : t1(_t1), t2(_t2) { }
void operator()() { t1(); t2(); }
};

template <class T1,class T2>
typename test_identity<Sum<T1,T2>,is_callable<T1>::value &&
is_callable<T2>::value>::type
operator+(T1 t1,T2 t2) {
return Sum<T1,T2>(t1,t2);
}
/* END CODE */

Basically, it only makes sense to have Sum<T1,T2if T1 and T2 are both
callable. I could easily insert a Boost static assert to ensure that
they are, but my issue is this: the overloaded operator+ still allows
any type to be sent to it, and then generates a compiler error if it's
not callable. So,

Constant<inta = 5; Constant<doubleb = 10.3;
(a+b)();

works, but

Constant<inta = 5;
(a+10.3)();

fails to compile, despite my implicit constructor, since it tries to
make Sum<Constant<int>,intand in doing so, tries to make
test<is_callable<int>::valuewhich is undefined.

I tried one more thing:

template <class T1,class T2>
Sum<T1,T2operator+(typename
test_identity<T1,is_callable<T1>::value>::type t1,
typename test_identity<T2,is_callable<T2>::value>::type t2) {
return Sum<T1,T2>(t1,t2);
}

but then my main() wouldn't compile because it couldn't implicitly
figure out that test_identity<T1,...>::type was really just T1.
Hopefully I've made my problem more clear. Thanks to those still
paying attention. Any ideas?

steve

Jul 13 '06 #9

P: n/a
* Steve Hicks:
Mi*************@tomtom.com wrote:
>Just add a test, using the typedef T type; trick again.

template<bool bstruct test; // undefined
template<struct test<true{ }; // defined specialization
template<typename T, bool b>
struct test_identity<T: test<b{
typedef T type;
};

template<class T>
increment_return_type<test_identity<T,
allowed_p<T>::value>::type>::type
operator++(T x);

That's a neat trick. Unfortunately, it doesn't quite do what I need.
Here is a small example:

/* BEGIN CODE */
#include <iostream>
template <bool bstruct test;
template <struct test<true{ };
template <class T, bool b>
struct test_identity : test<b{
typedef T type;
};
template <class Tstruct is_callable {
static const bool value = false;
};

template <class Tclass Constant {
T value;
public:
Constant(T t) : value(t) { }
void operator()()
{ std::cout << "value: " << value << std::endl; }
};
template <class Tstruct is_callable<Constant<T {
static const bool value = true;
};

// T1 and T2 must be callable
template <class T1, class T2class Sum {
T1 t1; T2 t2;
public:
Sum(T1 _t1, T2 _t2) : t1(_t1), t2(_t2) { }
void operator()() { t1(); t2(); }
};

template <class T1,class T2>
typename test_identity<Sum<T1,T2>,is_callable<T1>::value &&
is_callable<T2>::value>::type
operator+(T1 t1,T2 t2) {
return Sum<T1,T2>(t1,t2);
}
/* END CODE */

Basically, it only makes sense to have Sum<T1,T2if T1 and T2 are both
callable. I could easily insert a Boost static assert to ensure that
they are, but my issue is this: the overloaded operator+ still allows
any type to be sent to it, and then generates a compiler error if it's
not callable. So,

Constant<inta = 5; Constant<doubleb = 10.3;
(a+b)();

works, but

Constant<inta = 5;
(a+10.3)();

fails to compile, despite my implicit constructor, since it tries to
make Sum<Constant<int>,intand in doing so, tries to make
test<is_callable<int>::valuewhich is undefined.
You could try

template <class T1,class T2>
typename boost::enable_if_c<
is_callable<T1>::value && is_callable<T2>::value,
Sum<T1,T2>
>::type
operator+(T1 t1,T2 t2) { return Sum<T1,T2>(t1,t2); }

which I believe is specifically what you're asking for.

However, for your chosen problem this leads to a little (contained)
combinatorial explosion: it answers the question but does not solve the
problem in a reasonable way, and that's because you're not asking about
the problem but about a particular non-working way to solve it.

Better to simply make sure that anything that can behave as a constant
has a value:

#include <iostream>

template <class Tclass Constant {
T value;
public:
Constant(T t) : value(t) { }
double operator()() const
{ std::cout << "value: " << value << std::endl; return value; }
};

template< typename T T valueOf( T const& x )
{ return x; }
template< typename T T valueOf( Constant<Tconst& x )
{ return static_cast<T>( x() ); } // Cast due to use of 'double'.

template <class T1, class T2class Sum {
T1 t1; T2 t2;
public:
Sum(T1 _t1, T2 _t2) : t1(_t1), t2(_t2) { }
double operator()() const { return valueOf(t1) + valueOf(t2); }
};

template <class T1,class T2>
Sum<T1,T2operator+(T1 t1,T2 t2) { return Sum<T1,T2>(t1,t2); }

int main()
{
Constant<inta = 5; Constant<doubleb = 10.3;

std::cout << (a+b)() << std::endl;
std::cout << (a+10.3)() << std::endl;
}

Hth.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 13 '06 #10

P: n/a
Alf P. Steinbach wrote:
You could try

template <class T1,class T2>
typename boost::enable_if_c<
is_callable<T1>::value && is_callable<T2>::value,
Sum<T1,T2>
>::type
operator+(T1 t1,T2 t2) { return Sum<T1,T2>(t1,t2); }

which I believe is specifically what you're asking for.

However, for your chosen problem this leads to a little (contained)
combinatorial explosion: it answers the question but does not solve the
problem in a reasonable way, and that's because you're not asking about
the problem but about a particular non-working way to solve it.
That's a handy little tool. But you're right that it didn't quite
solve the problem. I still couldn't make the compiler figure out to do
the implicit cast.
Better to simply make sure that anything that can behave as a constant
has a value:
....
template< typename T T valueOf( T const& x )
{ return x; }
template< typename T T valueOf( Constant<Tconst& x )
{ return static_cast<T>( x() ); } // Cast due to use of 'double'.

template <class T1, class T2class Sum {
T1 t1; T2 t2;
public:
Sum(T1 _t1, T2 _t2) : t1(_t1), t2(_t2) { }
double operator()() const { return valueOf(t1) + valueOf(t2); }
};

template <class T1,class T2>
Sum<T1,T2operator+(T1 t1,T2 t2) { return Sum<T1,T2>(t1,t2); }
Well, I'm not quite sure what you're doing there with the static_cast.
But I did realize I was going about it all wrong. My solution is to
define

template <class T,bool b=is_callable<T>::valuestruct callable_cast {
typedef Constant<Ttype;
};
template <class Tstruct callable_cast<T,true{
typedef T type;
};

Then, I can write (using a helper to maybe improve readability)

template <class T1,class T2struct sum_result {
typedef Sum<typename callable_cast<T1>::type,
typename callable_cast<T2>::typetype;
};
template <class T1,class T2typename sum_result<T1,T2>::type
operator+(T1 t1,T2 t2) {
return typename sum_result<T1,T2>::type(t1,t2);
}

This way, the overloaded operator can take whatever it wants, and the
cast becomes explicit. My interest in this problem came from trying to
make a toy version of boost::lambda to see how it worked (cf. a post
from 4-5 days earlier), and realizing that I needed to enclose all my
literals in "make_constant(...)" so that they would cooperate with my
operators since they could only act on placeholder expressions. This
should solve that particular problem (now I just need to figure out how
to get the reference- and const-casting to work...)

Thank you all very much!
steve

Jul 13 '06 #11

This discussion thread is closed

Replies have been disabled for this discussion.