Specializing for array types
From codegorilla
Problem
A question once arose on the accu general mailing list as follows:
What is the correct way to express this intent? The intent is to provide
a general assign function, which is specialised for square arrays.
#include <cstddef>
namespace
{
template < typename T, std :: size_t sz > struct Block
{
typedef T type[ sz ][ sz ];
};
template < typename T > void assign( T & assignee, const T & value ) {}
template < typename T, std :: size_t sz >
void assign( typename Block< T, sz > :: type & assignee,
typename Block< T, sz > :: type const & value ) {}
}
Thanks, Rob.
Issues
The problem here is that there isn't one - not unless you are using a Microsoft Compiler, if you are then you'd have some problems. On a decent compiler the type matching would work on these multi-dimensional arrays.
Firstly a point of clarification should be made, in the example above it is not specialization, but basic overloading (specialization is only applicable on class/struct)
In the above example it seems that the Block type has just been engineered to try and get some type matching on the second assign function.
If you try to use the above code it will compile, it will even link - but is it what was intended:
Block<char,100>::type A,B; assign(A,B);
Which assign does it call, well it calls the first one, as thats the best match. If we remove the first assign to try to force the compiler to use the right version we now get the following compile errors (From VC++ 2005):
error C2783: 'void `anonymous-namespace'::assign(Block<T,sz>::type &,const Block<T,sz>::type &)' :
could not deduce template argument for 'T'
see declaration of '`anonymous-namespace'::assign'
error C2783: 'void `anonymous-namespace'::assign(Block<T,sz>::type &,const Block<T,sz>::type &)' :
could not deduce template argument for 'sz'
see declaration of '`anonymous-namespace'::assign'
Which is not good, and these are probably the problems the perp had in the first place.
Solution
So How do we solve it? How can we get the compiler to call our specialized assign method for a square array?
The solution is pretty straight forward, we need to specialize on an array type - and stop the first assign catching all the types.
boost type traits provides a nice little type checker called is_array, we can use this to specialize a class/struct on this result, the default is the catch all and the specialized (where is_array is true) to catch arrays.
This presents a problem though, as we are only interested in square arrays and is_array will catch all array types. This is relatively easy to solve, it just means we need to provide overloaded functions for those array types which are not square and pass it of to the default implementation.
#include <cstddef> #include <boost/type_traits/is_array.hpp> namespace { template < bool > struct assign_detail { template< typename T > static void assign( T & assignee, T const & value ) { // default assign } }; // specialization for is_array true types template <> struct assign_detail< true > { template < typename T, std::size_t sz > static void assign( T (&assignee)[sz][sz], T const (&value)[sz][sz] ) { // Square array } template < typename T, std::size_t sz > static void assign( T (&assignee)[sz], T const (&value)[sz] ) { // a 1 dimensional array // - don't want to specialize, so call the default assign_detail< false >::assign< T[sz] >(assignee,value); } template < typename T, std::size_t sz, std::size_t sz2 > static void assign( T (&assignee)[sz][sz2], T const (&value)[sz][sz2] ) { // an 2 dimensional array thats not square // - don't want to specialize, so call the default assign_detail< false >::assign< T[sz][sz2] >(assignee,value); } }; template < typename T > void assign( T & assignee, const T & value ) { // invoke the correct assign function in the assign_detail class/struct assign_detail<boost::is_array<T>::value>::assign(assignee,value); } } int main(int argc, char* argv[]) { char myblock[100][100]; char myblock2[100][100]; // calls square array assign assign( myblock, myblock2 ); char x,y; // calls default assign assign(x,y); char a[10],b[10]; // calls default asign via the overloaded 1 dimensional assign assign(a,b); char s[10][3]; char t[10][3]; // calls default asign via the overloaded non square 2 dimensional assign assign(s,t); return 0; }
Problem solved.
