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

Template argument matching

P: n/a

Please take a look at the following code:

template <typename T, typename Enable = void>
class A
{
public:
enum { value = 0 };
};

template <typename T>
class A<T, typename T::Enable>
{
public:
enum { value = 1 };
};

class B
{
public:
typedef void Enable;
};

class C
{
};

int main(int argc, char *argv[])
{
int b = A<B>::value; // should be 1
int c = A<C>::value; // should be 0
return 0;
}

The goal here was to create a class template called A in a way that
A<T>::value is 1 if T::Enable exists, and 0 otherwise. I've tried this
on MSVC++ 7.1, and it actually works, but only if T::Enable is exactly
the same type as the default value of the Enable template argument of
A. For example, if I change the typedef in B to
typedef int Enable; // int instead of void
then the value of variable b in main will be 0.

I'd like to know why's that. I thought that the specialization would be
a better match for all types where T::Enable exists, regardless of what
type it actually means. And I can't really see the connection with the
second template argument.

Imre

Jul 23 '05 #1
Share this Question
Share on Google+
13 Replies


P: n/a

Imre wrote:
Please take a look at the following code:

template <typename T, typename Enable = void>
class A
{
public:
enum { value = 0 };
};

template <typename T>
class A<T, typename T::Enable>
{
public:
enum { value = 1 };
};

class B
{
public:
typedef void Enable;
};

class C
{
};

int main(int argc, char *argv[])
{
int b = A<B>::value; // should be 1
int c = A<C>::value; // should be 0
return 0;
}

The goal here was to create a class template called A in a way that
A<T>::value is 1 if T::Enable exists, and 0 otherwise. I've tried this on MSVC++ 7.1, and it actually works, but only if T::Enable is exactly the same type as the default value of the Enable template argument of
A. For example, if I change the typedef in B to
typedef int Enable; // int instead of void
then the value of variable b in main will be 0.

I'd like to know why's that. I thought that the specialization would be a better match for all types where T::Enable exists, regardless of what type it actually means. And I can't really see the connection with the second template argument.

Imre


I'm guessing what you really want is a "trait":

.. #include <iostream>
..
.. template <typename T>
.. class Trait
.. {
.. public:
.. enum { ENABLE = 0 };
.. };

This defines a trait, by default it is zero. Now you can define your
'A' class like this:

.. template <typename T, typename T2 = Trait<T> >
.. class A
.. {
.. public:
.. enum { value = T2::ENABLE };
.. };

This gets 'value' from the 'ENABLE' enum in your Trait class (which is
zero by default). Now you can create your other classes:

.. class B { };
.. class C { };

Now, specialise Trait<T> so that 'ENABLE' is set to one for class 'B':

.. template <>
.. class Trait<B>
.. {
.. public:
.. enum { ENABLE = 1 };
.. };

This means that 'B' is now enabled for whatever you want to do in 'A'.

.. int main(int argc, char *argv[])
.. {
.. int b = A<B>::value; // should be 1
.. int c = A<C>::value; // should be 0
..
.. std::cout << b << " " << c << std::endl;
..
.. return 0;
.. }
This should print out "1 0" (as expected).

Hope this helps,
-shez-

Jul 23 '05 #2

P: n/a
I think it is possible that you are confusing default template
parameters with with partial template specialization.

If you look at your code above, neither of the lines:
int b = A<B>::value; // should be 1
int c = A<C>::value; // should be 0
explicitly supplies the 'Enable' parameter of your original template.

For that reason, you have implicitly written:

int b = A<B, void>::value;
int c = A<C, void>::value;

In other words, in both cases, your second template parameter is of
type void.

This means that when the compiler is deciding whether or not to use the
partial template specialization:
template <typename T>
class A<T, typename T::Enable>
{
public:
enum { value = 1 };
};
that T::Enable must be of type void in order to the specialization to
be chosen.

If you write:

class B
{
public:
typedef int Enable;
};

then the implicitly written line:

int b = A<B, void>::value;

does not use the partial template specialization:
template <typename T>
class A<T, typename T::Enable>
{
public:
enum { value = 1 };
};


because T::Enable (really B::Enable) is of type int, and the second
parameter of A<B, void> is of type void. In other words, since void is
the type of the second template parameter, a T::Enable of type int
cannot be applied, and the specialization cannot be chosen because of
the type conflict.

I hope this helps! =)

Michael Loritsch

Jul 23 '05 #3

P: n/a

Shezan Baig wrote:
This defines a trait, by default it is zero. Now you can define your
'A' class like this:

. template <typename T, typename T2 = Trait<T> >
. class A
. {
. public:
. enum { value = T2::ENABLE };
. };


Also, if you want to have a separate specialisation for ENABLE = 1 (as
in your original post), you can do this:

.. template <typename T,
.. typename T2 = Trait<T>,
.. int ENABLE = T2::ENABLE >
.. class A
.. {
.. public:
.. enum { value = 0 };
.. };
..
.. template <typename T, typename T2>
.. class A<T, T2, 1>
.. {
.. public:
.. enum { value = 1 };
.. };

This will give you the compile-time polymorphism I think you were
looking for in your original post.

Hope this helps,
-shez-

Jul 23 '05 #4

P: n/a

ri***@hotmail.com wrote:
I think it is possible that you are confusing default template
parameters with with partial template specialization.

If you look at your code above, neither of the lines:
int b = A<B>::value; // should be 1
int c = A<C>::value; // should be 0


explicitly supplies the 'Enable' parameter of your original template.

For that reason, you have implicitly written:

int b = A<B, void>::value;
int c = A<C, void>::value;

In other words, in both cases, your second template parameter is of
type void.


Ah, I see. From this point on, everything makes perfect sense. Somehow
I thought that the compiler would first look for specializations
(before binding the default value to the second argument), and then,
having found the specialization, deduce the second argument instead of
using the default one. Well, I was wrong.

So it seems that I'll have to provide some more information about why I
originally started to play with this.

I plan to use some hierarchy-wide traits, as presented by A.
Alexandrescu at
http://www.moderncppdesign.com/publi..._steroids.html.

Basically it looks like this:

template <class HierarchyId>
class HierarchyTraits
{
... most general traits here ...
};
template <class T>
class Traits
: public HierarchyTraits<T::HierarchyId>
{
// empty body - inherits all symbols from base class
};

Now if a root class of a class hierarchy has a nested class called
HierarchyId, you can specialize HierarchyTraits for Root::HierarchyId,
which is in effect the same as if you'd specialize Traits for all
descendants of Root. You can, of course, also directly specialize
Traits for specific types.

My problem with this solution is that the default implementation of
Traits explicitly references T::HierarchyId, thus cannot be used for
types that don't have the nested class HierarchyId. And that's exactly
what I would need. I'd like to have a default implementation that
doesn't need HierarchyId, and then have a specialization that should be
used for all types that do have HierarchyId. Only the specialization
would inherit from HierarchyTraits<T::HierarchyId>. The code in my
original post was an attempt to do this, but as you can see, I just
don't know enough about templates to get it right.

So how can it be done?

And thanks for all the answers so far.

Imre

Jul 23 '05 #5

P: n/a

Imre wrote:
My problem with this solution is that the default implementation of
Traits explicitly references T::HierarchyId, thus cannot be used for
types that don't have the nested class HierarchyId. And that's exactly what I would need. I'd like to have a default implementation that
doesn't need HierarchyId, and then have a specialization that should be used for all types that do have HierarchyId. Only the specialization
would inherit from HierarchyTraits<T::HierarchyId>. The code in my
original post was an attempt to do this, but as you can see, I just
don't know enough about templates to get it right.

So how can it be done?


The solution I posted earlier does something like this. It has a
default trait, and you can specialise the trait as your wish for
whatever class you want.

Hope this helps,
-shez-

Jul 23 '05 #6

P: n/a

Shezan Baig wrote:
Imre wrote:
The solution I posted earlier does something like this. It has a
default trait, and you can specialise the trait as your wish for
whatever class you want.
Ok, but how do I specialize it for a whole (possibly big) class
hierarchy at once? I don't want to create a separate specialization

forall subclasses. There's a lot of them, and all these specializations
would be exactly the same.


Ahh, I see. Sorry, I misread your earlier post. I'm not aware of how
we can generalise this. The best I can come up with now is to use a
macro:

.. #define ENABLE_TRAIT(T) \
.. template <> \
.. class Trait<B> \
.. { \
.. public: \
.. enum { ENABLE = 1 }; \
.. }; \

Now you just need to do:

ENABLE_TRAIT(B)
ENABLE_TRAIT(SomeClassThatDerivesFromB)

This is the best I can think of now. It's only one line per class, so
I don't think it will be *that* bad :)

Hope this helps,
-shez-

Jul 23 '05 #7

P: n/a

Shezan Baig wrote:
Shezan Baig wrote:
Imre wrote:
The solution I posted earlier does something like this. It has a
default trait, and you can specialise the trait as your wish for
whatever class you want.
Ok, but how do I specialize it for a whole (possibly big) class
hierarchy at once? I don't want to create a separate specialization

for
all subclasses. There's a lot of them, and all these specializations
would be exactly the same.


Ahh, I see. Sorry, I misread your earlier post. I'm not aware of

how we can generalise this. The best I can come up with now is to use a
macro:

. #define ENABLE_TRAIT(T) \
. template <> \
. class Trait<B> \

This should, of course, read:

.. class Trait<T>
. { \
. public: \
. enum { ENABLE = 1 }; \
. }; \

Now you just need to do:

ENABLE_TRAIT(B)
ENABLE_TRAIT(SomeClassThatDerivesFromB)

This is the best I can think of now. It's only one line per class, so I don't think it will be *that* bad :)

Hope this helps,
-shez-


Jul 23 '05 #8

P: n/a
> My problem with this solution is that the default implementation of
Traits explicitly references T::HierarchyId, thus cannot be used for
types that don't have the nested class HierarchyId. And that's exactly what I would need. I'd like to have a default implementation that
doesn't need HierarchyId, and then have a specialization that should be used for all types that do have HierarchyId. Only the specialization
would inherit from HierarchyTraits<T::HierarchyId>. The code in my
original post was an attempt to do this, but as you can see, I just
don't know enough about templates to get it right.

So how can it be done?


Well, okay, it's actually not that difficult. My problem was that I
wanted to specialize Traits for every types where T::HierarchyId
exists, whatever HierarchyId really is. Unfortunately, whatever is not
that easy to match. The solution is simply to put a typedef inside the
nested HierarchyId class, and specialize Traits based on the type value
of that typedef, insted of HierarchyId itself.

Imre

Jul 23 '05 #9

P: n/a

Imre wrote:
My problem with this solution is that the default implementation of
Traits explicitly references T::HierarchyId, thus cannot be used for types that don't have the nested class HierarchyId. And that's exactly
what I would need. I'd like to have a default implementation that
doesn't need HierarchyId, and then have a specialization that should be
used for all types that do have HierarchyId. Only the
specialization would inherit from HierarchyTraits<T::HierarchyId>. The code in my
original post was an attempt to do this, but as you can see, I just
don't know enough about templates to get it right.

So how can it be done?


Well, okay, it's actually not that difficult. My problem was that I
wanted to specialize Traits for every types where T::HierarchyId
exists, whatever HierarchyId really is. Unfortunately, whatever is

not that easy to match. The solution is simply to put a typedef inside the nested HierarchyId class, and specialize Traits based on the type value of that typedef, insted of HierarchyId itself.

Imre


Hi Imre,

I'm not sure I quite follow you. Can you post some code to demonstrate
this?

Thanks!

-shez-

Jul 23 '05 #10

P: n/a
> I'm not sure I quite follow you. Can you post some code to
demonstrate
this?


Sure.

template <typename T, typename Enable = void>
struct Traits
{
enum { value = 0 };
};

template <typename HierarchyId>
struct HierarchyTraits
{
};

template <typename T>
struct Traits<T, typename T::HierarchyId::Exists>:
public HierarchyTraits<typename T::HierarchyId>
{
};

/*
Now let's create a class and specialize Traits for it and all its
subclasses:
*/

class B
{
public:
class HierarchyId
{
typedef void Exists;
};
};

template <>
struct HierarchyTraits<B::HierarchyId>
{
enum { value = 1 };
};

/*
Now some subclasses of B. D1 defines its own subtree inside the class
hierarchy tree, with a different specialization of Traits. D2 just
uses B's version.
*/

class D1: public B
{
public:
class HierarchyId
{
typedef void Exists;
};
};

template <>
struct HierarchyTraits<D1::HierarchyId>
{
enum { value = 2 };
};

class D2: public B
{
};

/*
Let's also have a class that is not part of any hierarchies.
*/

class C
{
};

int main(int argc, char *argv[])
{
int b = Traits<B>::value;
int c = Traits<C>::value;
int d1 = Traits<D1>::value;
int d2 = Traits<D2>::value;
int i = Traits<int>::value;
return 0;
}

b and d2 should be 1 (value inherited from the HierarchyTraits
specialization we created for B::HierarchyId), d2 is 2 (D2's own
hierarchy specialization), c and i should be 0 (default, cause they
have no HierarchyId).

Actually that was all I wanted: to use hierarchy-wide traits while also
providing some defaults for types that are not part of any defined
hierarchies.

Imre

Jul 23 '05 #11

P: n/a
What compiler are you using? I am getting the following errors (using
Sun CC):
"test_heirarchytraits.cpp", line 18: Error: HierarchyId is not a member
of C.
"test_heirarchytraits.cpp", line 85: Where: While specializing
"Traits<C, void>".
"test_heirarchytraits.cpp", line 85: Where: Specialized in
non-template code.
"test_heirarchytraits.cpp", line 85: Error: value is not a member of
Traits<C, void>.
"test_heirarchytraits.cpp", line 18: Error: T is not a namespace or
class name.
"test_heirarchytraits.cpp", line 88: Where: While specializing
"Traits<int, void>".
"test_heirarchytraits.cpp", line 88: Where: Specialized in
non-template code.
"test_heirarchytraits.cpp", line 18: Error: HierarchyId is not defined.
"test_heirarchytraits.cpp", line 88: Where: While specializing
"Traits<int, void>".
"test_heirarchytraits.cpp", line 88: Where: Specialized in
non-template code.
"test_heirarchytraits.cpp", line 88: Error: value is not a member of
Traits<int, void>.
5 Error(s) detected.

Jul 23 '05 #12

P: n/a

Shezan Baig wrote:
What compiler are you using? I am getting the following errors (using Sun CC):
I've originally written it using MSVC++ 7.1.
Now I also tried it with gcc 3.4.3. Gcc didn't compile it at first, but
only because the Exists typedefs were private inside the HierarchyId
classes. After I've added public:, it compiled and worked well.


"test_heirarchytraits.cpp", line 18: Error: HierarchyId is not a member of C.
"test_heirarchytraits.cpp", line 85: Where: While specializing
"Traits<C, void>".
"test_heirarchytraits.cpp", line 85: Where: Specialized in
non-template code.
"test_heirarchytraits.cpp", line 85: Error: value is not a member of
Traits<C, void>.
"test_heirarchytraits.cpp", line 18: Error: T is not a namespace or
class name.
"test_heirarchytraits.cpp", line 88: Where: While specializing
"Traits<int, void>".
"test_heirarchytraits.cpp", line 88: Where: Specialized in
non-template code.
"test_heirarchytraits.cpp", line 18: Error: HierarchyId is not defined. "test_heirarchytraits.cpp", line 88: Where: While specializing
"Traits<int, void>".
"test_heirarchytraits.cpp", line 88: Where: Specialized in
non-template code.
"test_heirarchytraits.cpp", line 88: Error: value is not a member of
Traits<int, void>.
5 Error(s) detected.


Jul 23 '05 #13

P: n/a

Imre wrote:
Shezan Baig wrote:
What compiler are you using? I am getting the following errors (using
Sun CC):


I've originally written it using MSVC++ 7.1.
Now I also tried it with gcc 3.4.3. Gcc didn't compile it at first,

but only because the Exists typedefs were private inside the HierarchyId
classes. After I've added public:, it compiled and worked well.


Cool. I haven't got that version on me right now, but thanks!

-shez-

Jul 23 '05 #14

This discussion thread is closed

Replies have been disabled for this discussion.