472,975 Members | 1,363 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,975 software developers and data experts.

Template argument matching


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
13 1945

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
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

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

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

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

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

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
> 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

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
> 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
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

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

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 thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: Valeriu Catina | last post by:
Hi, given the next code: #define ENUM_CAST(x) int(x) template<class T,int N> class Array {
3
by: CoolPint | last post by:
Can anyone explain how I can make the following function accept an default arguement for the last parameter, which should be an optional functor? template <typename T, typename FUNCTOR> void...
2
by: marco | last post by:
the problem: I use a typedef inside a class template, than I use this type (dim_v<N1>::Type) to define the argument of a template function f but when I call this function from main, the compiler...
3
by: Capstar | last post by:
Hi NG, I am trying to get the attached piece of code to work, but I can't figure out what I'm doing wrong. To me it seems that when I don't pass an argument to x::do_something, it should use the...
4
by: George | last post by:
Dear All, I'm compiling the code below with IBM's xlC 6.0 and get the message, "rmspace.cpp", line 34.48: 1540-0298 (S) Template argument deduction cannot be performed using the function...
3
by: Tomás | last post by:
Let's say we have a variable length argument list function like so: unsigned GetAverage(unsigned a, unsigned b, ...); I wonder does the compiler go through the code looking for invocations of...
3
by: aiooua | last post by:
Any idea why the following code does not compile? ---- #include<iostream> #include<list> using namespace std; class Base { public: int val;
1
by: petschy | last post by:
hello, i've run into an error when qualifying a copy ctor 'explicit'. the strange thing is that i get a compiler error only if the class is a template and declare the variable as X<Zx = y....
5
by: krishnaroskin | last post by:
Hey all, I've been running into a problem with default values to template'd functions. I've boiled down my problem to this simple example code: #include<iostream> using namespace std; //...
2
by: DJRhino | last post by:
Was curious if anyone else was having this same issue or not.... I was just Up/Down graded to windows 11 and now my access combo boxes are not acting right. With win 10 I could start typing...
2
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 4 Oct 2023 starting at 18:00 UK time (6PM UTC+1) and finishing at about 19:15 (7.15PM) The start time is equivalent to 19:00 (7PM) in Central...
0
by: Aliciasmith | last post by:
In an age dominated by smartphones, having a mobile app for your business is no longer an option; it's a necessity. Whether you're a startup or an established enterprise, finding the right mobile app...
4
NeoPa
by: NeoPa | last post by:
Hello everyone. I find myself stuck trying to find the VBA way to get Access to create a PDF of the currently-selected (and open) object (Form or Report). I know it can be done by selecting :...
3
NeoPa
by: NeoPa | last post by:
Introduction For this article I'll be using a very simple database which has Form (clsForm) & Report (clsReport) classes that simply handle making the calling Form invisible until the Form, or all...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 1 Nov 2023 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM) Please note that the UK and Europe revert to winter time on...
3
by: nia12 | last post by:
Hi there, I am very new to Access so apologies if any of this is obvious/not clear. I am creating a data collection tool for health care employees to complete. It consists of a number of...
0
isladogs
by: isladogs | last post by:
The next online meeting of the Access Europe User Group will be on Wednesday 6 Dec 2023 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, Mike...
4
by: GKJR | last post by:
Does anyone have a recommendation to build a standalone application to replace an Access database? I have my bookkeeping software I developed in Access that I would like to make available to other...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.