A developer writes a function template for max, adds a full specialisation of it for pointers to const char, then also adds a plain overload taking two pointers to const char. Calls with string literals go to the overload and the specialisation is never reached. Explain the rule that produces this and what to write instead.

A developer writes a function template for max, adds a full specialisation of it for pointers to const char, then also adds a plain overload taking two pointers to const char. Calls with string literals go to the overload and the specialisation is never reached. Explain the rule that produces this and what to write instead.

Approach: Order the steps of overload resolution: which candidates are collected, at what point a specialisation of a chosen candidate is considered, and what a specialisation is attached to.

Overload resolution runs over the primary templates and the non-template functions first, and only after a candidate wins does the compiler check whether that candidate has a specialisation, so a non-template overload that wins takes the call and the specialisation of the template is unreachable. A function template specialisation does not participate in overload resolution in its own right, it is attached to one primary template, and selecting a different primary or a plain function skips it entirely. The guidance that follows is to overload function templates instead of specialising them, and where behaviour must vary by type, to put the dispatch in a class template, since class templates do support partial specialisation and function templates do not.

Follow-up: What does the same rule imply for customising std::hash for your own type by specialisation versus by an overloaded free function?

Key concepts: overload resolution, function template specialisation, primary template, partial specialisation.