cppcoder
cppcoder

Reputation: 23105

How to implement generic max function?

I know this is because the return type of the template function is same as that of the first argument (T).
How can I modify this template so that, it behaves correctly in all cases?

#include <iostream>
using namespace std;
template <typename T, typename U>
T max(T x, U y)
{
    return x>y ? x : y;
}

int main()
{
    cout<<max(17.9,17)<<"\n";
    cout<<max(17,17.9)<<"\n";
}

Output:

17.9  
17

Upvotes: 4

Views: 4482

Answers (5)

PiotrNycz
PiotrNycz

Reputation: 24402

This is answer for C++03. For C++11 - use auto/decltype (see other answers).

You have to create another template: template CommonNumericType<T1,T2>:

template <typename L, typename R>
typename CommonNumericType<T1,T2>::Type max(L x, R y)
{
    return x>y ? x : y;
}

And specialize this CommonNumericType for every possible pair of numeric types:

template <typename L, typename R>
struct CommonNumericType;
template <typename T>
struct CommonNumericType<T,T> {
   typedef T Type;
};
template <typename L>
struct CommonNumericType<L,long double> {
   typedef long double Type;
};
template <typename R>
struct CommonNumericType<long double,R> {
   typedef long double Type;
};
// ...
template <>
struct CommonNumericType<int,short> {
   typedef int Type;
};
// and many others stuff

I can think of making some numeric types hierarchy - float types before int types - and so on. Because <number of numeric types>^2 is quite big number:

template <typename T>
struct NumericTypeOrder;
template <>
struct NumericTypeOrder<long double> { enum { VALUE = 1 }; };
template <>
struct NumericTypeOrder<double> { enum { VALUE = 2 }; };
template <>
struct NumericTypeOrder<float> { enum { VALUE = 3 }; };
template <>
struct NumericTypeOrder<unsigned long long> { enum { VALUE = 4 }; };
// etc for all numeric types - where signed char is last one...

template <typename L, typename R, bool L_bigger_than_R>
struct CommonNumericTypeImpl;
template <typename L, typename R>
struct CommonNumericTypeImpl<L,R,true> {
  typedef L type;
};
template <typename L, typename R>
struct CommonNumericTypeImpl<L,R,false> {
  typedef R type;
};
template <typename L, typename R>
struct CommonNumericType 
: CommonNumericTypeImpl<L,R,NumericTypeOrder<L>::value >= NumericTypeOrder<R>::value > {
};

Or just use macro:

#define max(l,r) ((l) >= (r) ? (l) : (r))

Much simpler, isn't it?

Upvotes: 2

marcinj
marcinj

Reputation: 49986

with c++11 its easy, use std::common_type<> trait:

template <typename T, typename U>
typename std::common_type<T,U>::type max(T x, U y) /// why not const& T and const& U ?
{
    return x>y ? x : y;
}

common_type<> uses decltype keyword and declval<> trait which are both new since c++11

Upvotes: 4

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361372

The behaviour for your implementation is correct, though you may not want that output. The problem with the return type in your code.

You may want to use trailing-return type if you can use C++11:

template <typename T, typename U>
auto max(T x, U y) -> decltype(x>y ? x : y)    //C++11 only
{
    return x>y ? x : y;
}

which would give this output:

17.9
17.9

I hope that is the desired output.

Online demo : http://ideone.com/2Sh5Y

Upvotes: 10

Jens Kilian
Jens Kilian

Reputation: 425

In C++11 you can use type inference (with auto, late return types and decltype):

template <typename T, typename U>
auto max(T x, U y) -> decltype(x>y ? x : y)
{
  return x>y ? x : y;
}

Upvotes: 1

David Schwartz
David Schwartz

Reputation: 182763

The output is correct. You never specified a type, so complaining that it didn't use the type you wanted it to use is not reasonable. If you want a specific type, you have to ensure both parameters are that type.

You could cast the first parameter to a double. Or you could specifically invoke
max<double, double>. You could also specialize max<int, double> and similar combinations if you really, really wanted to.

Upvotes: 3

Related Questions