limiting a template class to certain types ?

M

mast2as

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<float> floatSomethng; // yes it's fine
AClass<int> intSomething; // no we don't want to allow the creation of
that object

Thank you, -mark
 
R

red floyd

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<float> floatSomethng; // yes it's fine
AClass<int> intSomething; // 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<double> ad;
AClass<int> ai; // error
}
 
V

Victor Bazarov

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<float> floatSomethng; // yes it's fine
AClass<int> intSomething; // 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 T> class 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<float> af;
AClass<int> af; // error
}

V
 
S

Steve Hicks

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

template<class T> class 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<float> af;
AClass<int> af; // 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
 
V

Victor Bazarov

Steve said:
[..] 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
 
M

Michiel.Salters

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 b> struct 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
 
S

Steve Hicks

Just add a test, using the typedef T type; trick again.

template<bool b> struct 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 b> struct test;
template <> struct test<true> { };
template <class T, bool b>
struct test_identity : test<b> {
typedef T type;
};
template <class T> struct is_callable {
static const bool value = false;
};

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

// T1 and T2 must be callable
template <class T1, class T2> class 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,T2> if 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<int> a = 5; Constant<double> b = 10.3;
(a+b)();

works, but

Constant<int> a = 5;
(a+10.3)();

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

I tried one more thing:

template <class T1,class T2>
Sum<T1,T2> operator+(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
 
A

Alf P. Steinbach

* Steve Hicks:
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 b> struct test;
template <> struct test<true> { };
template <class T, bool b>
struct test_identity : test<b> {
typedef T type;
};
template <class T> struct is_callable {
static const bool value = false;
};

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

// T1 and T2 must be callable
template <class T1, class T2> class 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,T2> if 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<int> a = 5; Constant<double> b = 10.3;
(a+b)();

works, but

Constant<int> a = 5;
(a+10.3)();

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

You could try

template <class T1,class T2>
typename boost::enable_if_c<
is_callable said:
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 T> class 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<T> const& x )
{ return static_cast<T>( x() ); } // Cast due to use of 'double'.

template <class T1, class T2> class 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,T2> operator+(T1 t1,T2 t2) { return Sum<T1,T2>(t1,t2); }

int main()
{
Constant<int> a = 5; Constant<double> b = 10.3;

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

Hth.
 
S

Steve Hicks

Alf said:
You could try

template <class T1,class T2>
typename boost::enable_if_c<

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<T> const& x )
{ return static_cast<T>( x() ); } // Cast due to use of 'double'.

template <class T1, class T2> class 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,T2> operator+(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>::value> struct callable_cast {
typedef Constant<T> type;
};
template <class T> struct callable_cast<T,true> {
typedef T type;
};

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

template <class T1,class T2> struct sum_result {
typedef Sum<typename callable_cast<T1>::type,
typename callable_cast<T2>::type> type;
};
template <class T1,class T2> typename 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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,149
Latest member
Vinay Kumar Nevatia0
Top