Suhail Akhtar
Suhail Akhtar

Reputation: 629

Type conversion in template

I am using a templated function and a non-templated one. Code is given below

#include <iostream>
using namespace std;

int maxOfTwo1(int a,int b)
{
    return a>b?a:b;
}
template<class T>
T maxOfTwo(T a,T b)
{
    return a>b?a:b;
}
int main()
{
    cout<<maxOfTwo1(3,6.3);//works fine
    cout<<maxOfTwo(3,6.3);//gives error
}

In first function 6.3 is getting converted to 6, but can anyone explain why it doesn't happen in second function also? Second function works also the same just having template.

Upvotes: 2

Views: 436

Answers (2)

Shafik Yaghmour
Shafik Yaghmour

Reputation: 158449

In your first case the type of the parameters are known (int) and the arguments are converted to that type (int), which is well-formed.

In your second case the compiler must deduce T but it ends with conflicting deductions and therefore it fails.

We can see this from [temp.deduct.type]p2 which says (emphasis mine):

In some cases, the deduction is done using a single set of types P and A, in other cases, there will be a set of corresponding types P and A. Type deduction is done independently for each P/A pair, and the deduced template argument values are then combined. If type deduction cannot be done for any P/A pair, or if for any pair the deduction leads to more than one possible set of deduced values, or if different pairs yield different deduced values, or if any template argument remains neither deduced nor explicitly specified, template argument deduction fails.

The definitions of P and A can be found in [temp.deduct.call]p1:

Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below. If removing references and cv-qualifiers from P gives std::initializer_list for some P' and the argument is an initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context ([temp.deduct.type]).

max66 gave some possible solutions, i.e. using different template parameters combined with common_type.

Upvotes: 1

max66
max66

Reputation: 66190

Deduction conflict.

Given

template<class T>
T maxOfTwo(T a,T b)
{
    return a>b?a:b;
}

If you call maxOfTwo(3,6.3), the first value is a int; the second one is a float.

The compiler must deduce a single T type and don't know if select int or float.

The case of maxOfTwo1() is different because the type of argument is fixed to int and the compiler has just to convert the float value to a int value.

If you can use at least C++14, you can solve using two template types and a returning auto type

template <typename T1, typename T2>
auto maxOfTwo (T1 a, T2 b)
 { return a>b?a:b; }

In C++11 is a little more verbose and less elegant

template <typename T1, typename T2>
auto maxOfTwo (T1 a, T2 b) -> decltype( a>b ? a : b )
 { return a>b?a:b; }

or you can use std::common_type (as suggested by Yakk)

template <typename T1, typename T2>
typename std::common_type<T1, T2>::type maxOfTwo (T1 a, T2 b)
 { return a>b?a:b; }

Otherwise you can explicit the correct type calling the function

maxOfTwo<int>(3, 6.3);

Upvotes: 3

Related Questions