How to specialize a template function to allow it to take a char array as an argument?

// problem.cpp: #include <string> template<typename T> void func(const T & v); int main() { int i; float f; char * cp; char ca[4]; func(i); func(f); func(cp); func(std::string("std::string")); func(ca); func("string_literal"); return 0; } // problem2.cpp #include <string> template<typename T> void func(const T & v); // undefined reference to `void func<int>(int const&)' template<> void func<int>(const int & v) { } // undefined reference to `void func<float>(float const&)' template<> void func<float>(const float & v) { } // undefined reference to `void func<char*>(char* const&)' template<> void func<char *>(char * const & v) { } // void func<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) template<> void func<std::string>(std::string const & v) { } // undefined reference to `void func<char [4]>(char const (&) [4])' // ??? // undefined reference to `void func<char [15]>(char const (&) [15])' // ??? 

Found two solutions:

a) in problem2.cpp:

 template<> void func<char[4]>(const char (&v)[4]) { } template<> void func<char[15]>(const char (&v)[15]) { } 

b) in problem.cpp:

 template<typename T, unsigned N> void func(const T (&v)[N]) { func(v+0); } and then in problem2.cpp, add the newly missing template<> void func<const char *>(const char * const & v) { } 

Sorry akappa, I had to edit it again to clarify that these are two independent solutions ...

akappa: My only way to add something to this question is to edit it. I also cannot comment and cannot add an answer. May have something to do with "Stack Overflow requires JavaScript from another domain that is locked or not loaded." Which I don’t know how to resolve, because I don’t know what exactly SO is trying to tell me about it.

+4
source share
1 answer

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 & ) { // code goes here } template void func<int>( int const & ); template void func<double>( double const & ); //... 
+3
source

All Articles