Stack Exchange
Is it valid to overload function with extra non-deducible template parameter?
The following code compiles and works fine with gcc (9), clang (11) and msvc (16.28):
template <class A>
struct X {
    A a;

    constexpr X(A a) : a{a} { }
};

template <class A>
constexpr auto fn(X<A> const& x) {
    return X<A>(x.a - 1);
}


template <class Xs, class A>
constexpr auto fn(X<A> const& x) {
    return Xs(x.a - 1);
}

constexpr X<int> x1{3};
constexpr auto x2 = fn(x1);
constexpr auto x3 = fn<X<double>>(x1);

There are two fn functions with identical declarations except for the extra Xs parameter in the second one.
I'd like to be sure that this is standard-accepted and not something that these compilers provide as extra? Since all 3 do, I'd guess this would be standard, but you never know.
I'd also like to know if my assumptions as to why this work/would be standard are correct:

in the call fn(x1), Xs cannot be deduced so the second overload of fn is discarded gently (SFINAE?)?
in the call fn<Xs>(x1), the first overload would then be fn(X<X<int>>) which does not match the argument x1, thus the first overload is also discarded?


Mostly, it is not SFINAE which rejects second overload, as there are not even a substitution.


There are two fn functions with identical declarations except for the extra Xs parameter in the second one.

That's fine, you can overload function templates using different arguments or different template arguments.


in the call fn(x1), Xs cannot be deduced so the second overload of fn is discarded gently (SFINAE?)?


Yes, except it's not SFINAE, but overload resolution. The S in SFINAE stands for substitution. When a template argument can't be deduced, no substitution takes place.
It's described in [temp.over]/1:

... For each function template, if the argument deduction and checking succeeds, the template-arguments (deduced and/or explicit) are used to synthesize the declaration of a single function template specialization which is added to the candidate functions set to be used in overload resolution.

With regard to the second bullet (fn<X<double>>(x1)), both templates can synthesize a valid declaration, but X<int> isn't convertible to X<X<double>>, so the second overload is selected as the only viable one.
Note: when multiple template overloads are viable, partial ordering is performed to determine the most specialized template.

Thanks, I was pretty sure it was not SFINAE due to the "non-substitution". If I understand the quote it means that in fn(x1), you can synthesize fn<int>(X<int>) from the first overload but you cannot synthesize anything from the second one because you cannot deduce Xs, while in fn<X<int>>(x1), you synthesize both fn<X<int>>(X<int>) and fn<X<int>, int>(X<int>) and only the second overload matches, right?

When multiple overloads are viable, template specializations or not, they are first compared based on conversion sequences. Only if that comparison yields a result of, roughly speaking, "equally good", do we get to the tie breakers, one of them being partial ordering.

Interestingly, this would be visible in this case if the second call were fn<X<int>>(x1). In this case, both overloads are viable, as there is a conversion from X<int> to X<X<int>> for the first overload, but the second one has a better conversion (identity), so the second one is chosen. And this happens even though neither template is more specialized than the other according to the partial ordering rules. cc @Holt
