Search code examples
c++pointerstemplatesdecltype

funtion with const pointer template type parameter not called for arguments of non-const pointer type


From my understanding, a const pointer function argument should be able to accept both a const and non-const pointer. In the latter case, it's illegal to modify the value through the pointer.

Supposed I have the following template functions

template <typename T1, typename T2>
decltype(auto) plus(T1 a, T2 b) {
    return a + b;
}

template <typename T1, typename T2>
decltype(auto) plus(const T1* a, const T2* b) {
    return *a + *b;
}

int main() {

    int a {2}, b {3};
    std::cout << plus(a, b) << std::endl;

    int *first { new int {2} }, *second { new int {3} };

    // it seems here that the return type is deduced to be void*?

    std::cout << plus(first, second) << std::endl;
}

The second function call correctly calls the plus template function that accepts pointer arguments but deduces the return type to be void*

However, if I change the template signature to

template <typename T1, typename T2>
decltype(auto) plus(T1* a, T2* b) {
    return *a + *b;
}

this produces the correct result.

What's going on here?


Solution

  • plus(first, second) calls the first template overload, because it is a better match.

    After template argument deduction (yielding T1 == T2 == int* for the fist and T1 == T2 == int for the second) both overloads are viable. But, for each of the two parameters, the first overload only requires an lvalue-to-rvalue conversion, while the second one requires lvalue-to-rvalue conversion, followed by a qualification conversion (adding const).

    Therefore the implicit conversion sequences of both parameters are better for the first overload and it is therefore chosen by overload resolution. The function template partial ordering is not even considered.

    Calling the first function template with T1 == T2 == int* is however ill-formed, because a + b is not allowed if both a and b are pointer types. Your compiler should already give you an error here.


    With the alternative signature for the second template overload, it won't require the additional qualification conversion mentioned above and the implicit conversion sequences for the parameters of the two overloads will be identically.

    Therefore function template partial ordering will be considered to decide which overload to choose and because the second template is more specialized than the first one, the second one will be chosen.

    With T1 == T2 == int it will then work as expected, because *a + *b adds two int, not two pointers, yielding a return type of int, because *a + *b is a prvalue.