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

function template

P: n/a
Hi all,
I was working with a simple function template to find the min of two values.
But since I would like the two values to be different (type) I dont know
what kind of value (type) it will return. I tried to write something like
this:

template <class Type1, class Type2, class Type3>
Type3 findMin(Type1 x, Type2 y){

return (x < y) ? x : y;
}

but it says it cannot deduce template argument for 'Type3'
Can anyone help? Thanx
Jul 22 '05 #1
Share this Question
Share on Google+
31 Replies


P: n/a
"nikola" <se******@here.lol> wrote in message
news:Zk***********************@news3.tin.it...
Hi all,
I was working with a simple function template to find the min of two values. But since I would like the two values to be different (type) I dont know
what kind of value (type) it will return. I tried to write something like
this:

template <class Type1, class Type2, class Type3>
Type3 findMin(Type1 x, Type2 y){

return (x < y) ? x : y;
}

but it says it cannot deduce template argument for 'Type3'
Can anyone help? Thanx


Hello

Try calling it as:

findMin<int, int, int>(1, 2);

Like that you have specified all the types.

--

Elias
Jul 22 '05 #2

P: n/a
nikola wrote:
Hi all,
I was working with a simple function template to find the min of two
values. But since I would like the two values to be different (type) I
dont know what kind of value (type) it will return. I tried to write
something like this:

template <class Type1, class Type2, class Type3>
Type3 findMin(Type1 x, Type2 y){

return (x < y) ? x : y;
}

but it says it cannot deduce template argument for 'Type3'


Well, first of all, the return type of a function is fixed at compile
time. And how would the compiler know which one to return?
Seond, the 2nd and 3rd argument of the ?: operator need to be of the
same type, too, so one of your arguments is converted to the other's
type anyway. Similar for operator<. So you can just write:

template <class Type>
Type findMin(Type x, Type y){
return (x < y) ? x : y;
}

Jul 22 '05 #3

P: n/a
nikola wrote:
Hi all,
I was working with a simple function template to find the min of two values.
But since I would like the two values to be different (type) I dont know
what kind of value (type) it will return. I tried to write something like
this:

template <class Type1, class Type2, class Type3>
Type3 findMin(Type1 x, Type2 y){

return (x < y) ? x : y;
}

but it says it cannot deduce template argument for 'Type3'
Can anyone help? Thanx


This is a classic template issue.

What you really want is :

template <typename T1, typename T2>
typename Promote<T1,T2>::type min( const T1 & x, const T2 & y )
{
return x < y ? x : y;
}

Where Promote is a template that will perform the right "promotion" for
T1 and T2.

If the "typeof()" function was standard, you could simply do this:
template <typename T1, typename T2>
struct Promote
{
typedef typeof( T1() + T2() ) type;
};

.... but alas, things are not so simple in the standard world.

We need to figure it out ourselves so we have to use partial template
specialization.

Start with the basic template:
template <typename T1, typename T2>
struct Promote
{
};

// the same types are the same ! cool

template <typename T1>
struct Promote<T1,T1>
{
typedef T1 type;
};

// specify all the type promotions ...

template <> struct Promote<unsigned char,char> { typedef int type; };
template <> struct Promote<signed char,char> { typedef int type; };
template <> struct Promote<short,char> { typedef int type; };
template <> struct Promote<unsigned short,char> { typedef int type; };
template <> struct Promote<int,char> { typedef int type; };
template <> struct Promote<unsigned int,char> { typedef unsigned int
type; };
template <> struct Promote<long,char> { typedef long type; };
template <> struct Promote<unsigned long,char> { typedef unsigned long
type; };
template <> struct Promote<long long,char> { typedef long long type; };
template <> struct Promote<unsigned long long,char> { typedef unsigned
long long type; };
template <> struct Promote<float,char> { typedef float type; };
template <> struct Promote<double,char> { typedef double type; };
template <> struct Promote<long double,char> { typedef long double type; };
template <> struct Promote<char,unsigned char> { typedef int type; };
template <> struct Promote<signed char,unsigned char> { typedef int type; };
template <> struct Promote<short,unsigned char> { typedef int type; };
template <> struct Promote<unsigned short,unsigned char> { typedef int
type; };
template <> struct Promote<int,unsigned char> { typedef int type; };
template <> struct Promote<unsigned int,unsigned char> { typedef
unsigned int type; };
template <> struct Promote<long,unsigned char> { typedef long type; };
template <> struct Promote<unsigned long,unsigned char> { typedef
unsigned long type; };
template <> struct Promote<long long,unsigned char> { typedef long long
type; };
template <> struct Promote<unsigned long long,unsigned char> { typedef
unsigned long long type; };
template <> struct Promote<float,unsigned char> { typedef float type; };
template <> struct Promote<double,unsigned char> { typedef double type; };
template <> struct Promote<long double,unsigned char> { typedef long
double type; };
template <> struct Promote<char,signed char> { typedef int type; };
template <> struct Promote<unsigned char,signed char> { typedef int type; };
template <> struct Promote<short,signed char> { typedef int type; };
template <> struct Promote<unsigned short,signed char> { typedef int
type; };
template <> struct Promote<int,signed char> { typedef int type; };
template <> struct Promote<unsigned int,signed char> { typedef unsigned
int type; };
template <> struct Promote<long,signed char> { typedef long type; };
template <> struct Promote<unsigned long,signed char> { typedef unsigned
long type; };
template <> struct Promote<long long,signed char> { typedef long long
type; };
template <> struct Promote<unsigned long long,signed char> { typedef
unsigned long long type; };
template <> struct Promote<float,signed char> { typedef float type; };
template <> struct Promote<double,signed char> { typedef double type; };
template <> struct Promote<long double,signed char> { typedef long
double type; };
template <> struct Promote<char,short> { typedef int type; };
template <> struct Promote<unsigned char,short> { typedef int type; };
template <> struct Promote<signed char,short> { typedef int type; };
template <> struct Promote<unsigned short,short> { typedef int type; };
template <> struct Promote<int,short> { typedef int type; };
template <> struct Promote<unsigned int,short> { typedef unsigned int
type; };
template <> struct Promote<long,short> { typedef long type; };
template <> struct Promote<unsigned long,short> { typedef unsigned long
type; };
template <> struct Promote<long long,short> { typedef long long type; };
template <> struct Promote<unsigned long long,short> { typedef unsigned
long long type; };
template <> struct Promote<float,short> { typedef float type; };
template <> struct Promote<double,short> { typedef double type; };
template <> struct Promote<long double,short> { typedef long double type; };
template <> struct Promote<char,unsigned short> { typedef int type; };
template <> struct Promote<unsigned char,unsigned short> { typedef int
type; };
template <> struct Promote<signed char,unsigned short> { typedef int
type; };
template <> struct Promote<short,unsigned short> { typedef int type; };
template <> struct Promote<int,unsigned short> { typedef int type; };
template <> struct Promote<unsigned int,unsigned short> { typedef
unsigned int type; };
template <> struct Promote<long,unsigned short> { typedef long type; };
template <> struct Promote<unsigned long,unsigned short> { typedef
unsigned long type; };
template <> struct Promote<long long,unsigned short> { typedef long long
type; };
template <> struct Promote<unsigned long long,unsigned short> { typedef
unsigned long long type; };
template <> struct Promote<float,unsigned short> { typedef float type; };
template <> struct Promote<double,unsigned short> { typedef double type; };
template <> struct Promote<long double,unsigned short> { typedef long
double type; };
template <> struct Promote<char,int> { typedef int type; };
template <> struct Promote<unsigned char,int> { typedef int type; };
template <> struct Promote<signed char,int> { typedef int type; };
template <> struct Promote<short,int> { typedef int type; };
template <> struct Promote<unsigned short,int> { typedef int type; };
template <> struct Promote<unsigned int,int> { typedef unsigned int type; };
template <> struct Promote<long,int> { typedef long type; };
template <> struct Promote<unsigned long,int> { typedef unsigned long
type; };
template <> struct Promote<long long,int> { typedef long long type; };
template <> struct Promote<unsigned long long,int> { typedef unsigned
long long type; };
template <> struct Promote<float,int> { typedef float type; };
template <> struct Promote<double,int> { typedef double type; };
template <> struct Promote<long double,int> { typedef long double type; };
template <> struct Promote<char,unsigned int> { typedef unsigned int
type; };
template <> struct Promote<unsigned char,unsigned int> { typedef
unsigned int type; };
template <> struct Promote<signed char,unsigned int> { typedef unsigned
int type; };
template <> struct Promote<short,unsigned int> { typedef unsigned int
type; };
template <> struct Promote<unsigned short,unsigned int> { typedef
unsigned int type; };
template <> struct Promote<int,unsigned int> { typedef unsigned int type; };
template <> struct Promote<long,unsigned int> { typedef unsigned long
type; };
template <> struct Promote<unsigned long,unsigned int> { typedef
unsigned long type; };
template <> struct Promote<long long,unsigned int> { typedef long long
type; };
template <> struct Promote<unsigned long long,unsigned int> { typedef
unsigned long long type; };
template <> struct Promote<float,unsigned int> { typedef float type; };
template <> struct Promote<double,unsigned int> { typedef double type; };
template <> struct Promote<long double,unsigned int> { typedef long
double type; };
template <> struct Promote<char,long> { typedef long type; };
template <> struct Promote<unsigned char,long> { typedef long type; };
template <> struct Promote<signed char,long> { typedef long type; };
template <> struct Promote<short,long> { typedef long type; };
template <> struct Promote<unsigned short,long> { typedef long type; };
template <> struct Promote<int,long> { typedef long type; };
template <> struct Promote<unsigned int,long> { typedef unsigned long
type; };
template <> struct Promote<unsigned long,long> { typedef unsigned long
type; };
template <> struct Promote<long long,long> { typedef long long type; };
template <> struct Promote<unsigned long long,long> { typedef unsigned
long long type; };
template <> struct Promote<float,long> { typedef float type; };
template <> struct Promote<double,long> { typedef double type; };
template <> struct Promote<long double,long> { typedef long double type; };
template <> struct Promote<char,unsigned long> { typedef unsigned long
type; };
template <> struct Promote<unsigned char,unsigned long> { typedef
unsigned long type; };
template <> struct Promote<signed char,unsigned long> { typedef unsigned
long type; };
template <> struct Promote<short,unsigned long> { typedef unsigned long
type; };
template <> struct Promote<unsigned short,unsigned long> { typedef
unsigned long type; };
template <> struct Promote<int,unsigned long> { typedef unsigned long
type; };
template <> struct Promote<unsigned int,unsigned long> { typedef
unsigned long type; };
template <> struct Promote<long,unsigned long> { typedef unsigned long
type; };
template <> struct Promote<long long,unsigned long> { typedef long long
type; };
template <> struct Promote<unsigned long long,unsigned long> { typedef
unsigned long long type; };
template <> struct Promote<float,unsigned long> { typedef float type; };
template <> struct Promote<double,unsigned long> { typedef double type; };
template <> struct Promote<long double,unsigned long> { typedef long
double type; };
template <> struct Promote<char,long long> { typedef long long type; };
template <> struct Promote<unsigned char,long long> { typedef long long
type; };
template <> struct Promote<signed char,long long> { typedef long long
type; };
template <> struct Promote<short,long long> { typedef long long type; };
template <> struct Promote<unsigned short,long long> { typedef long long
type; };
template <> struct Promote<int,long long> { typedef long long type; };
template <> struct Promote<unsigned int,long long> { typedef long long
type; };
template <> struct Promote<long,long long> { typedef long long type; };
template <> struct Promote<unsigned long,long long> { typedef long long
type; };
template <> struct Promote<unsigned long long,long long> { typedef
unsigned long long type; };
template <> struct Promote<float,long long> { typedef float type; };
template <> struct Promote<double,long long> { typedef double type; };
template <> struct Promote<long double,long long> { typedef long double
type; };
template <> struct Promote<char,unsigned long long> { typedef unsigned
long long type; };
template <> struct Promote<unsigned char,unsigned long long> { typedef
unsigned long long type; };
template <> struct Promote<signed char,unsigned long long> { typedef
unsigned long long type; };
template <> struct Promote<short,unsigned long long> { typedef unsigned
long long type; };
template <> struct Promote<unsigned short,unsigned long long> { typedef
unsigned long long type; };
template <> struct Promote<int,unsigned long long> { typedef unsigned
long long type; };
template <> struct Promote<unsigned int,unsigned long long> { typedef
unsigned long long type; };
template <> struct Promote<long,unsigned long long> { typedef unsigned
long long type; };
template <> struct Promote<unsigned long,unsigned long long> { typedef
unsigned long long type; };
template <> struct Promote<long long,unsigned long long> { typedef
unsigned long long type; };
template <> struct Promote<float,unsigned long long> { typedef float
type; };
template <> struct Promote<double,unsigned long long> { typedef double
type; };
template <> struct Promote<long double,unsigned long long> { typedef
long double type; };
template <> struct Promote<char,float> { typedef float type; };
template <> struct Promote<unsigned char,float> { typedef float type; };
template <> struct Promote<signed char,float> { typedef float type; };
template <> struct Promote<short,float> { typedef float type; };
template <> struct Promote<unsigned short,float> { typedef float type; };
template <> struct Promote<int,float> { typedef float type; };
template <> struct Promote<unsigned int,float> { typedef float type; };
template <> struct Promote<long,float> { typedef float type; };
template <> struct Promote<unsigned long,float> { typedef float type; };
template <> struct Promote<long long,float> { typedef float type; };
template <> struct Promote<unsigned long long,float> { typedef float
type; };
template <> struct Promote<double,float> { typedef double type; };
template <> struct Promote<long double,float> { typedef long double type; };
template <> struct Promote<char,double> { typedef double type; };
template <> struct Promote<unsigned char,double> { typedef double type; };
template <> struct Promote<signed char,double> { typedef double type; };
template <> struct Promote<short,double> { typedef double type; };
template <> struct Promote<unsigned short,double> { typedef double type; };
template <> struct Promote<int,double> { typedef double type; };
template <> struct Promote<unsigned int,double> { typedef double type; };
template <> struct Promote<long,double> { typedef double type; };
template <> struct Promote<unsigned long,double> { typedef double type; };
template <> struct Promote<long long,double> { typedef double type; };
template <> struct Promote<unsigned long long,double> { typedef double
type; };
template <> struct Promote<float,double> { typedef double type; };
template <> struct Promote<long double,double> { typedef long double
type; };
template <> struct Promote<char,long double> { typedef long double type; };
template <> struct Promote<unsigned char,long double> { typedef long
double type; };
template <> struct Promote<signed char,long double> { typedef long
double type; };
template <> struct Promote<short,long double> { typedef long double type; };
template <> struct Promote<unsigned short,long double> { typedef long
double type; };
template <> struct Promote<int,long double> { typedef long double type; };
template <> struct Promote<unsigned int,long double> { typedef long
double type; };
template <> struct Promote<long,long double> { typedef long double type; };
template <> struct Promote<unsigned long,long double> { typedef long
double type; };
template <> struct Promote<long long,long double> { typedef long double
type; };
template <> struct Promote<unsigned long long,long double> { typedef
long double type; };
template <> struct Promote<float,long double> { typedef long double type; };
template <> struct Promote<double,long double> { typedef long double
type; };

//
// and now the min template ...

template <typename T1, typename T2>
typename Promote<T1,T2>::type min( const T1 & x, const T2 & y )
{
return x < y ? x : y;
}

That's a bit long winded. However, some people might arge that type
promotion is a bad thing and that the developer really needs to make
sure that the parameters to min (or max) are the same type, in which
case the template becomes easy to write.

Jul 22 '05 #4

P: n/a
nikola wrote:

template <class Type1, class Type2, class Type3>
Type3 findMin(Type1 x, Type2 y){

return (x < y) ? x : y;
}

but it says it cannot deduce template argument for 'Type3'
Can anyone help? Thanx


#define findMin(x, y) ((x) < (y)) ? (x) : (y))

Otherwise you have to write template code that duplicates the promotion
rules, and that will be rather long-winded.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 22 '05 #5

P: n/a
Pete Becker wrote:
nikola wrote:
template <class Type1, class Type2, class Type3>
Type3 findMin(Type1 x, Type2 y){

return (x < y) ? x : y;
}

but it says it cannot deduce template argument for 'Type3'
Can anyone help? Thanx

#define findMin(x, y) ((x) < (y)) ? (x) : (y))

Otherwise you have to write template code that duplicates the promotion
rules, and that will be rather long-winded.


Gee, that's neat. Until somebody tries to

findMin( ++i, j );
Jul 22 '05 #6

P: n/a
Jeff Schwab wrote:

Pete Becker wrote:
nikola wrote:
template <class Type1, class Type2, class Type3>
Type3 findMin(Type1 x, Type2 y){

return (x < y) ? x : y;
}

but it says it cannot deduce template argument for 'Type3'
Can anyone help? Thanx

#define findMin(x, y) ((x) < (y)) ? (x) : (y))

Otherwise you have to write template code that duplicates the promotion
rules, and that will be rather long-winded.


Gee, that's neat. Until somebody tries to

findMin( ++i, j );


Yes, that's a limitation. The proposed template solutions also have
limitations: one requires you to specify the return type at each call
site, one requires a loooong list of template specializations, and one
won't compile. Which do you prefer?

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 22 '05 #7

P: n/a
Pete Becker wrote:
Jeff Schwab wrote:
Pete Becker wrote:
nikola wrote:
template <class Type1, class Type2, class Type3>
Type3 findMin(Type1 x, Type2 y){

return (x < y) ? x : y;
}

but it says it cannot deduce template argument for 'Type3'
Can anyone help? Thanx
#define findMin(x, y) ((x) < (y)) ? (x) : (y))

Otherwise you have to write template code that duplicates the promotion
rules, and that will be rather long-winded.


Gee, that's neat. Until somebody tries to

findMin( ++i, j );

Yes, that's a limitation. The proposed template solutions also have
limitations: one requires you to specify the return type at each call
site, one requires a loooong list of template specializations, and one
won't compile. Which do you prefer?


The one with the long list of specializations, which can be (and now
have been) written in a single file, and henceforth hardly need be
noticed again.
Jul 22 '05 #8

P: n/a
Jeff Schwab wrote:

The one with the long list of specializations, which can be (and now
have been) written in a single file, and henceforth hardly need be
noticed again.


Except when it has to be modified to handle additional types, which is a
common failing of brute force techniques.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 22 '05 #9

P: n/a
> > The one with the long list of specializations, which
can be (and now have been) written in a single file,
and henceforth hardly need be noticed again.


Except when it has to be modified to handle additional
types, which is a common failing of brute force
techniques.


True, but in this case we are forced to choose between
the lesser of two evils: the dreaded macro or a brute
force mess of template specializations. The best
compromise I have found is from the Blitz++ library.
The solution doesn't always adhere to C++ promotion
rules, but comes very close without (as much) brute
force:

http://cvs.sourceforge.net/viewcvs.p...litz/promote.h

This common problem seems to indicate that C++ could
be made more template metaprogramming friendly.
Jul 22 '05 #10

P: n/a
Sorry, this is the link I meant to post:

http://cvs.sourceforge.net/viewcvs.p....h?view=markup
Jul 22 '05 #11

P: n/a
Derek wrote:
> > The one with the long list of specializations, which
> > can be (and now have been) written in a single file,
> > and henceforth hardly need be noticed again. >
> Except when it has to be modified to handle additional
> types, which is a common failing of brute force
> techniques.


True, but in this case we are forced to choose between
the lesser of two evils: the dreaded macro or a brute
force mess of template specializations.


Yup. The first is a dogmatic objection and the second is a technical
issue.
This common problem seems to indicate that C++ could
be made more template metaprogramming friendly.


Or that dogma isn't a good basis for design decisions.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 22 '05 #12

P: n/a
> > True, but in this case we are forced to choose between
the lesser of two evils: the dreaded macro or a brute
force mess of template specializations.


Yup. The first is a dogmatic objection and the second is
a technical issue.
This common problem seems to indicate that C++ could be
made more template metaprogramming friendly.


Or that dogma isn't a good basis for design decisions.


In this case I will concede the point and agree that a macro
is a perfectly acceptable solution. However, in less trivial
cases where macros can't be used, I still think C++ could be
improved in this department. But that's a whole different
discussion.
Jul 22 '05 #13

P: n/a
Pete Becker wrote:
Jeff Schwab wrote:
The one with the long list of specializations, which can be (and now
have been) written in a single file, and henceforth hardly need be
noticed again.

Except when it has to be modified to handle additional types, which is a
common failing of brute force techniques.


No, it *never* needs to be modified to handle additional types. C++
only has so many built-in types. If other types have non-intuitive
semantics, specializations can be added separately. It already is
common to provide certainly traits classes as part of a complete type
(numeric_limits, iterator_traits, etc.). If additional, custom traits
classes are desired, they can be written separately.

The nature of the C++ type system has undergone a great deal of careful
scrutiny, and allows new functionality (like the OP's find_min function)
to be provided in easy-to-use libraries that can be extended as
necessary. The implementation of such functionality frequently does
require much more code than a C-like, macro-based solution, but this is
a reflection of the fact that so much more attention is being payed to
the details and potential use of the types involved.
Jul 22 '05 #14

P: n/a
Jeff Schwab wrote:

Pete Becker wrote:
Jeff Schwab wrote:
The one with the long list of specializations, which can be (and now
have been) written in a single file, and henceforth hardly need be
noticed again.

Except when it has to be modified to handle additional types, which is a
common failing of brute force techniques.


No, it *never* needs to be modified to handle additional types. C++
only has so many built-in types.


Any one compiler that you use only has so many built-in types. But
different compilers, including other versions of whichever one you're
using, can support different sets of types (e.g. __int64 on several
compilers), and as C++ evolves it may well support additional standard
types (long long comes immediately to mind). And, of course, there are
also user-defined arithmetic classes. Never say never.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 22 '05 #15

P: n/a
Pete Becker wrote:
Jeff Schwab wrote:
Pete Becker wrote:
Jeff Schwab wrote:
The one with the long list of specializations, which can be (and now
have been) written in a single file, and henceforth hardly need be
noticed again.
Except when it has to be modified to handle additional types, which is a
common failing of brute force techniques.


No, it *never* needs to be modified to handle additional types. C++
only has so many built-in types.

Any one compiler that you use only has so many built-in types. But
different compilers, including other versions of whichever one you're
using, can support different sets of types (e.g. __int64 on several
compilers), and as C++ evolves it may well support additional standard
types (long long comes immediately to mind). And, of course, there are
also user-defined arithmetic classes. Never say never.


I say never. The fact that a compiler may (and many do) provide types
beyond those specified by the standard doesn't mean that specializations
for those types need to be defined in the same file as the usual
specializations. These additional types can be specialized separately,
just like any other, or a library vendor may opt to include them in the
same file for convenience.
Jul 22 '05 #16

P: n/a
Jeff Schwab wrote:

I say never. The fact that a compiler may (and many do) provide types
beyond those specified by the standard doesn't mean that specializations
for those types need to be defined in the same file as the usual
specializations.
Nice try, but nobody said anything about "the same file." The point is
that you have to add a bunch more specializaitons for every type that
isn't in your original implementation.
These additional types can be specialized separately,
just like any other,
That doesn't solve the problem of having to add them for any additional
types you want to support.
or a library vendor may opt to include them in the
same file for convenience.


So are you planning to pay a library vendor to maintain your code, or
will they just do it out of the kindness of their hearts?

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 22 '05 #17

P: n/a
Pete Becker wrote:
Jeff Schwab wrote:
I say never. The fact that a compiler may (and many do) provide types
beyond those specified by the standard doesn't mean that specializations
for those types need to be defined in the same file as the usual
specializations.

Nice try,


Try at what? I'm expressing my own opinion, that's all. If you
disagree, I wish you nothing but the best with your own approach. Of
course, I'm not likely to license any code from you that has a
macro-based interface... ;)
but nobody said anything about "the same file." The point is
that you have to add a bunch more specializaitons for every type that
isn't in your original implementation.


Nonsense. Default implementations handle most cases. Additional
specializations may be provided for performance reasons, or in rare
cases may be truly necessary, but they are just part of generic
programming, as functions are part of procedural programming.
These additional types can be specialized separately,
just like any other,

That doesn't solve the problem of having to add them for any additional
types you want to support.


You don't have to add specializations for all new types. Only the types
with which particular traits classes might be needed, and for which the
default template definitions don't work properly.
or a library vendor may opt to include them in the
same file for convenience.

So are you planning to pay a library vendor to maintain your code, or
will they just do it out of the kindness of their hearts?


Eh? Necessary specializations of traits classes for new types aren't
"maintenance," they're separate entities, and may be sold separately.
If you're arguing that traits classes are more hassle than they're
worth, then there's really not much point in continuing this conversation.
Jul 22 '05 #18

P: n/a
Jeff Schwab wrote:

Pete Becker wrote:
Jeff Schwab wrote:
I say never. The fact that a compiler may (and many do) provide types
beyond those specified by the standard doesn't mean that specializations
for those types need to be defined in the same file as the usual
specializations.

Nice try,


Try at what?


At changing the subject.
but nobody said anything about "the same file." The point is
that you have to add a bunch more specializaitons for every type that
isn't in your original implementation.


Nonsense. Default implementations handle most cases.


Look again: every one of those approximately 100 specialiations is
needed. Every time you compare two objects of different types you need a
specialization. The default implementation only handles objects of the
same type. And that's the problem: this approach requires a
specialization for every pair of distinct types.
Additional
specializations may be provided for performance reasons, or in rare
cases may be truly necessary, but they are just part of generic
programming, as functions are part of procedural programming.
No, this approach requires a template specialization for every pair of
distinct types that the program compares.
These additional types can be specialized separately,
just like any other,

That doesn't solve the problem of having to add them for any additional
types you want to support.


You don't have to add specializations for all new types.


I didn't say "for all new types". I said "for any additional types YOU
WANT TO SUPPORT." (emphasis added for the reading impaired)
Only the types
with which particular traits classes might be needed, and for which the
default template definitions don't work properly.
The original question, if you recall, was about handling pairs of
arguments of different types. This approach requires a template
specialization for every such pair. So, yes, it's "only" those types,
but they're the only ones that are interesting. Handling pairs of the
same type is simple.
or a library vendor may opt to include them in the
same file for convenience.

So are you planning to pay a library vendor to maintain your code, or
will they just do it out of the kindness of their hearts?


Eh? Necessary specializations of traits classes for new types aren't
"maintenance," they're separate entities, and may be sold separately.


Word games. Adding stuff in order to make your template work is
maintenance, regardless of whether it's "sold separately." Not that it's
at all clear why someone would be selling these things: the question was
how to write a template that compares two objects of different types,
and the proposed solution consists of a long list of template
specializations. Who do you think is going to sell template
specializations for class MyType? And how will they know that you need
to compare Mytype and YourType objects, so they can provide the
appropriate specializations?
If you're arguing that traits classes are more hassle than they're
worth, then there's really not much point in continuing this conversation.


I've said nothing about traits classes. What I said is that writing
N*(N-1) template specializations to handle all pairs of N argument types
isn't my idea of clean code. I prefer to let the compiler do that kind
of work -- it's much better at it. (Note that the sample implementation
doesn't get the platform-dependent promotion semantics right, while the
compiler almost certainly does).

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 22 '05 #19

P: n/a
Pete Becker wrote:

Jeff Schwab wrote:

Pete Becker wrote:
nikola wrote:

>template <class Type1, class Type2, class Type3>
>Type3 findMin(Type1 x, Type2 y){
>
>return (x < y) ? x : y;
>}
>
>but it says it cannot deduce template argument for 'Type3'
>Can anyone help? Thanx
#define findMin(x, y) ((x) < (y)) ? (x) : (y))

Otherwise you have to write template code that duplicates the promotion
rules, and that will be rather long-winded.


Gee, that's neat. Until somebody tries to

findMin( ++i, j );


Yes, that's a limitation. The proposed template solutions also have
limitations: one requires you to specify the return type at each call
site, one requires a loooong list of template specializations, and one
won't compile. Which do you prefer?


Another problem I've encountered with the macro approach is that the
compiler's choice of promotion is not always appropriate.
Here is a quick example:

//////////////////////////////////////////////////////
#include <iostream>

template <typename T>
T const& self_ref(T const& v) {
return v;
}

#define find_max_(a, b) ((a) > (b)? (a) : (b))

int main() {
unsigned int a = 0xffffffff;

double x = self_ref(find_max_(a, 5.f));

std::cout.setf(std::ios::fixed);
std::cout<<"a:\t"<<a<<std::endl;
std::cout<<"x:\t"<<x<<std::endl;

return 0;
}
//////////////////////////////////////////////////////
Let's say int is 4 bytes, float is IEEE 754 single, double is double format.
The output that I receive with several compilers is:
a: 4294967295
x: 4294967296.000000

This is not what I want.

Apparently, the float-integral conversion in the macro expression doesn't
consider a promotion to double; and it shouldn't (please correct me
if I'm wrong).

The purpose of self_ref is that without it gcc 3.3.3 did manage to get
the "right" result by optimising away the intermediate float value.

Denis
Jul 22 '05 #20

P: n/a
Denis Remezov wrote:

The output that I receive with several compilers is:
a: 4294967295
x: 4294967296.000000

This is not what I want.


That's the result of the ?: operator, regardless of whether you use it
directly in your code, through a macro, or through a template. Since one
argument is unsigned and the other float, the unsigned is converted to
float. Its value is not exactly representable on your system.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 22 '05 #21

P: n/a
Pete Becker wrote:

Denis Remezov wrote:

The output that I receive with several compilers is:
a: 4294967295
x: 4294967296.000000

This is not what I want.


That's the result of the ?: operator, regardless of whether you use it
directly in your code, through a macro, or through a template. Since one
argument is unsigned and the other float, the unsigned is converted to
float. Its value is not exactly representable on your system.


Oh well, I understand that. The point was that you cannot always rely
on the compiler to do the "right" promotion automatically. Sometimes
you need to do that semi-manually (e.g. via type traits).
I, at least, cannot see how you can do it automatically in a standard
way.

Denis
Jul 22 '05 #22

P: n/a
Pete Becker wrote:
....
I've said nothing about traits classes. What I said is that writing
N*(N-1) template specializations to handle all pairs of N argument types
isn't my idea of clean code. I prefer to let the compiler do that kind
of work -- it's much better at it. (Note that the sample implementation
doesn't get the platform-dependent promotion semantics right, while the
compiler almost certainly does).


Time for typeof() then ?
Jul 22 '05 #23

P: n/a
Gianni Mariani wrote:

Pete Becker wrote:
...
I've said nothing about traits classes. What I said is that writing
N*(N-1) template specializations to handle all pairs of N argument types
isn't my idea of clean code. I prefer to let the compiler do that kind
of work -- it's much better at it. (Note that the sample implementation
doesn't get the platform-dependent promotion semantics right, while the
compiler almost certainly does).


Time for typeof() then ?


Maybe. But that answers a different question: "How might I write this
code in the future?", versus "How can I write this code?" Which was your
original point...

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 22 '05 #24

P: n/a
Denis Remezov wrote:

Oh well, I understand that. The point was that you cannot always rely
on the compiler to do the "right" promotion automatically. Sometimes
you need to do that semi-manually (e.g. via type traits).


If by "the 'right' promotion" you mean that you can't rely on the
compiler to do something other than what it's supposed to do, I suppose
you're right. The compiler follows the language rules. If you don't want
the result that the language rules provide for ?: then you have to write
code that does something different. Saying the magic words "type traits"
doesn't change that.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 22 '05 #25

P: n/a
Pete Becker wrote:

Denis Remezov wrote:

Oh well, I understand that. The point was that you cannot always rely
on the compiler to do the "right" promotion automatically. Sometimes
you need to do that semi-manually (e.g. via type traits).


If by "the 'right' promotion" you mean that you can't rely on the
compiler to do something other than what it's supposed to do, I suppose
you're right. The compiler follows the language rules. If you don't want
the result that the language rules provide for ?: then you have to write
code that does something different. Saying the magic words "type traits"
doesn't change that.


I couldn't figure out what it doesn't change. Maybe I'm dyslexic.
template <typename T0, typename T1>
struct promote {};

//platform-dependent
template <>
struct promote<unsigned int, float> {
typedef double type;
};

//...

template <typename T_arg0, typename T_arg1>
typename promote<T_arg0, T_arg1>::type find_max(T_arg0 x, T_arg1 y) {
typedef typename promote<T_arg0, T_arg1>::type T_ret;
T_ret xp = (T_ret)x;
T_ret yp = (T_ret)y;
return xp > yp? xp : yp;
}

Denis
Jul 22 '05 #26

P: n/a
Pete Becker wrote:
Jeff Schwab wrote:
Pete Becker wrote:
Jeff Schwab wrote:
I say never. The fact that a compiler may (and many do) provide types
beyond those specified by the standard doesn't mean that specializations
for those types need to be defined in the same file as the usual
specializations.
Nice try,
Try at what?

At changing the subject.


That wasn't my intent.
but nobody said anything about "the same file." The point is
that you have to add a bunch more specializaitons for every type that
isn't in your original implementation.


Nonsense. Default implementations handle most cases.

Look again: every one of those approximately 100 specialiations is
needed. Every time you compare two objects of different types you need a
specialization. The default implementation only handles objects of the
same type. And that's the problem: this approach requires a
specialization for every pair of distinct types.


Not so. Get the Josuttis book on templates and read the chapter will he
covers exactly this case (Promotion_traits).
Additional
specializations may be provided for performance reasons, or in rare
cases may be truly necessary, but they are just part of generic
programming, as functions are part of procedural programming.

No, this approach requires a template specialization for every pair of
distinct types that the program compares.


That's not true.
These additional types can be specialized separately,
just like any other,
That doesn't solve the problem of having to add them for any additional
types you want to support.


You don't have to add specializations for all new types.

I didn't say "for all new types". I said "for any additional types YOU
WANT TO SUPPORT." (emphasis added for the reading impaired)


Getting upset and yelling won't help.

I read what you wrote, and I responded to what you seemed to mean. I'm
sorry for any confusion on my part.

Anyway, it's still not true. As a naive first pass, a default template
to find the minimum of two values of different types might just choose
the larger type, as determined by sizeof. This won't always do the
right thing, but it's a reasonable default.
Only the types
with which particular traits classes might be needed, and for which the
default template definitions don't work properly.

The original question, if you recall, was about handling pairs of
arguments of different types. This approach requires a template
specialization for every such pair. So, yes, it's "only" those types,
but they're the only ones that are interesting. Handling pairs of the
same type is simple.

or a library vendor may opt to include them in the
same file for convenience.
So are you planning to pay a library vendor to maintain your code, or
will they just do it out of the kindness of their hearts?


Eh? Necessary specializations of traits classes for new types aren't
"maintenance," they're separate entities, and may be sold separately.

Word games.


No, meaningful distinctions.
Adding stuff in order to make your template work is
maintenance, regardless of whether it's "sold separately."
Why do you think of it as "adding stuff?" Traits classes needing
specialization for a given type aren't just add-ons.
Not that it's
at all clear why someone would be selling these things: the question was
how to write a template that compares two objects of different types,
and the proposed solution consists of a long list of template
specializations. Who do you think is going to sell template
specializations for class MyType?
The designer or vendor of class MyType, if not someone else.
And how will they know that you need
to compare Mytype and YourType objects, so they can provide the
appropriate specializations?
They don't have to know that. At a minimum, they might specialize the
traits classes that are already part of the standard library (if
necessary), e.g. numeric_limits for a large integer type. If a vendor
also is providing a way to compare two values of a given type, it seems
reasonable that they might also specialize a common traits classes
related to comparisons, e.g. Promotion_traits. Of course, a client also
can request particular specializations, just as they might request any
other part of an interface explicitly.
If you're arguing that traits classes are more hassle than they're
worth, then there's really not much point in continuing this conversation.

I've said nothing about traits classes. What I said is that writing
N*(N-1) template specializations to handle all pairs of N argument types
isn't my idea of clean code.


That may be what you meant, but it's not what you said.
I prefer to let the compiler do that kind
of work -- it's much better at it.
Not so. The compiler does the right thing in many, but not all, cases.
(Note that the sample implementation
doesn't get the platform-dependent promotion semantics right, while the
compiler almost certainly does).


Then platform-dependent types should rely on the compiler to do the
right thing. The compiler won't always do the right thing, though, and
it should be possible for users to specialize the cases without changing
the user-visible interface.
Jul 22 '05 #27

P: n/a

"Jeff Schwab" <je************@comcast.net> wrote in message
news:Tb********************@comcast.com...
Look again: every one of those approximately 100 specialiations is
needed. Every time you compare two objects of different types you need a
specialization. The default implementation only handles objects of the
same type. And that's the problem: this approach requires a
specialization for every pair of distinct types.
Not so. Get the Josuttis book on templates and read the chapter will he
covers exactly this case (Promotion_traits).


If the resulting type of operator could be only type of one of the operand
it is possible IMHO to go without traits. Something like

template<bool Cond,typename T,typename U> struct If{ typedef T type; };
template<typename T,typename U> struct If<false,T,U>{ typedef U type; };

template<typename T,typename U>
struct CondOp
{
static char (&helper(T,U ...))[1];
static char (&helper(T,U,U))[2];
static T t();
static U u();
typedef typename If< sizeof( helper( t(), u(), 0 ? t() : u() ) )==1, T, U::type type;

};

template<typename T,typename U>
typename CondOp<T,U>::type max(T a,U b)
{
return a>b ? a: b;
}

Cheers,
Serge
Jul 22 '05 #28

P: n/a
Jeff Schwab wrote:

Anyway, it's still not true. As a naive first pass, a default template
to find the minimum of two values of different types might just choose
the larger type, as determined by sizeof. This won't always do the
right thing, but it's a reasonable default.


Sigh. I was talking about the templates in Gianni's original message,
which is the approach that you said you liked. Now you say its
weaknesses are solved by yet another approach, with yet another set of
weaknesses. Sorry, I'm not going to play any longer.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 22 '05 #29

P: n/a
Denis Remezov wrote:

I couldn't figure out what it doesn't change. Maybe I'm dyslexic.


No, but you didn't say what you had in mind. The discussion was about
determining the return type, and changing that doesn't affect the type
that ?: produces.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 22 '05 #30

P: n/a
Pete Becker wrote:
Jeff Schwab wrote:
Anyway, it's still not true. As a naive first pass, a default template
to find the minimum of two values of different types might just choose
the larger type, as determined by sizeof. This won't always do the
right thing, but it's a reasonable default.

Sigh. I was talking about the templates in Gianni's original message,
which is the approach that you said you liked. Now you say its
weaknesses are solved by yet another approach, with yet another set of
weaknesses.


That is not what I said. I like Gianni's approach just fine. The
problem you claimed with the approach was that new specializations would
need to be provided for any possible pair of types whose values would be
compared. That's not true.
Sorry, I'm not going to play any longer.


Don't be sorry. I'm not.
Jul 22 '05 #31

P: n/a
Jeff Schwab wrote:

That is not what I said. I like Gianni's approach just fine. The
problem you claimed with the approach was that new specializations would
need to be provided for any possible pair of types whose values would be
compared. That's not true.


Okay, time for the bottom line.

#define findMin(x, y) (((x) < (y)) ? (x) : (y))

This took about thirty seconds to write. It works for all sensible
types. Its limitations can be expressed in a simple declarative
sentence: arguments should not have side effects. It's simple enough
that its correctness can be determined by inspection.

Your solution starts with a template specialization for arguments of the
same type, plus about a hundred specializations for mixed arithmetic
types. As written it gives wrong answers on some platforms. Your
solution for additional arithmetic types is that library vendors will do
whatever is needed. You haven't addressed the portability problem.
You've also suggested that additional types can be accommodated by
adding "default" templates, perhaps using sizeof as a first cut, but you
haven't provided that code. In short, you don't have a complete
implementation and you don't have a design specification. As a result,
it's not possible to assess how well your solution works, nor what its
limitations are. Further, testing this brute force solution will require
a brute force test suite, and it will require testing on every compiler
that you want to use.

The macro is clearly the better solution.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 22 '05 #32

This discussion thread is closed

Replies have been disabled for this discussion.