Reputation: 23105
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
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
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
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
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
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