Search code examples
c++templatesc++11c++14type-promotion

Why doesn't my templated function promote 'int' to 'T', where 'T' = 'double'?


I have a class templated with typename T. It contains a function,

template <typename T, size_t a>
myClass<T,a> operator+(myClass<T,a> lhs, const T& rhs) {
    return lhs += rhs;
}

myClass<T,a> myClass<T,a>::operator+=(const T& rhs) {
    // Do addition, depends on 'a'.
    return *this;
}

When I call this with, for example

myClass<double, 2> myObj_double_2(constructor args);
myObj_double_2 = myObj_double_2 + 5.2;

I have no problem.

If I however call

myObj_double_2 = myObj_double_2 + 5;

Then the compiler gives me a message like - No match for 'operator+' (operand types are 'myClass<double, 2ul>' and 'int'). Candidates are ... note: deduced conflicting types for parameter 'const T' ('double' and 'int').

Can I write the code in some way to allow additional types to be passed that have a conversion to T (since, for example, double(5) is a valid constructor call)?


Solution

  • When you are using template argument deduction, all deductions for one template parameter must have the same result.

    In your case, the two deductions for T produce double and int, which are not the same, and so deduction fails.

    What you can do is only use one function argument for template argument deduction, and make the other one undeduced:

    template <typename T, std::size_t A>
    void foo(myClass<T, A> arg1, typename std::common_type<T>::type arg2);
    //                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    

    Note that std::common_type<T>::type is essentially just T, but because the type of arg2 is now a dependent type (its name appears to the right of a ::), it is not deduced. Therefore, only the first argument takes part in deduction and produces T = double unambiguously, and then the second function parameter just has type double, and the usual conversions take place.

    As a rule of thumb, template argument deduction does not cross ::.