473,396 Members | 1,706 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 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 1970

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; //...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

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.