Function specializations are complex in that they must exactly match the type of argument. In the case of an array (a string literal is also an array), the compiler will perform type inference and find out what the exact type is, and then it will look for that particular character in the program.
In particular, the type ca is equal to char[4] , therefore, when calling the template, the deduced type is T == char[4] , and the signature of the function that it expects to find is void func<>( const char (&)[4] ) As for your two decisions, then they are completely different approaches. In the first case, you specialize in a template for the specific types that are used. This will become painful, since with each new string literal or type that you use, you will need to manually add a specialization. This, by the way, is the reason why templates should (most often) be defined in the header, so you do not need to specify all possible template arguments in explicit instances (*) ...
The second solution is completely different. In this case, you create a second unrelated (to some extent) base template. This basic template gets a pointer to the first element of the array and calls the original template with it, effectively changing types (and losing information in the process: size is now lost). At this stage, all calls with arrays will correspond to this second pattern and will be redirected as calls to the original pattern with a pointer, which will reduce the need to specialize in array size (the compiler allows these specializations).
Also note that if you want to allow char arrays to be passed, the dispatcher template does not have to accept all types, it can have one non-type argument:
template <std::size_t N> void f( const char (&a)[N] ) { f( &a[0] ); }
Summarizing:
Avoid template specializations of functions as they are cumbersome to solve. An array type includes a size, which in turn means that you need to specialize in each size of the potential array. Alternatively, you can add an extra template that will send the original template by converting to a pointer.
* Please note: if the implementation of each and all specializations is the same, you can avoid specializing (and copying the code) while maintaining the same behavior by providing a template definition in .cpp and then manually creating a template
template <typename T> void func( T const & ) {