The explicit specialization is not a second template as your question title implies. An explicit specialization of a template is itself neither a template nor a templated entity.
It isn't an additional overload either as your use of "second function" in the question body implies. It doesn't participate in either overload resolution or template argument deduction.
The only thing it does (essentially) is to replace the generic function body of the original function template for an explicitly given set of template arguments, which in your case are the template arguments with T = const X&
, which is deduced by comparing T
to const X&
in template<> void func(const X& x)
since you didn't provide the template arguments for the explicit specialization explicitly (e.g. with template<> void func<const X&>(const X& x)
).
The type of x
as an expression in func(x)
is X
. The type of y
as an expression in func(y)
is const X
. Expressions never have reference types. References are stripped from the type of an entity referred to by an id-expression. Instead expressions have value categories which in some way correspond to the idea of reference-qualifications (see decltype
), but in particular an id-expression naming a variable is always a lvalue, regardless of the reference qualification of the variable's type.
Template argument deduction for the call func(/*...*/)
is always done using the template itself, not an explicit specialization. Because the template has a parameter of type T t
, T
will never be deduced to a reference type. Instead it will deduce T = X
for both func(x)
and func(y)
. The top level const
on the argument will not be deduced against a non-reference parameter.
Your explicit specialization however is for T = const X&
, not T = X
. So it won't be used.
The way you wrote the template and specialization you can only use the explicit specialization by explicit providing the template argument, e.g.
func<const X&>(x);
func<const X&>(y);
If you want the explicit specialization to be used also for deduced template arguments, either make the specialization's parameter X x
instead of const X& x
, which will cause the function call to pass the argument by-value or alternatively write the function template with function parameter T&
, const T&
or T&&
, so that the parameter is always deduced to a reference and the call will be by-reference.
Which exact form you want in the latter case depends on what behavior you want. For example with const T&
, after deduction the parameter is always const
-qualified, so that your explicit specialization will match for any X
type argument, while for T&
and T&&
it won't if the argument isn't const
-qualified itself. const T&
and T&&
allow passing rvalue arguments, while T&
won't.
For a reference, see e.g. https://en.cppreference.com/w/cpp/language/function_template and https://en.cppreference.com/w/cpp/language/template_argument_deduction and linked pages. This might be too information-dense as an entry point though, in which case one of the books going into depth about templates listed here might help.