0

I'm trying to develop a function with different behaviour for arrays and pointers. It could be achieved for classes by partial specialization, but it doesn't work on functions!

int i = 0;
some_func( &i ); // pointer
int arr[ 3 ] = { 0 };
some_func( arr ); // array

In second case inside of function some_func parameter type is int*, and there is no way to find out, that it's actually int[3].

On the other hand, if I use class template specialization, I'd have to specify array type explicitly:

template< typename T >
struct S
{
  static void some_func( T t ) { ... }
};

template< typename T, size_t N >
struct S< T[ N ] >
{
  static void some_func( T t[ N ] ) { ... }
};

// ...............

int arr[ 3 ] = { 0 };
S< int[ 3 ] >::some_func( arr ); // Works,
// but specifying type explicitly is not convenient

For now I've solved the problem with macro define (to be precise, I only need precise sizeof for arrays).

Is there any way to solve it without turning to the dark side of macro definitions?

perror
  • 7,071
  • 16
  • 58
  • 85
Igor Semenov
  • 483
  • 5
  • 13

2 Answers2

5

Take the array by reference:

template< typename T >
void some_func( T *t ) { ... }

template< typename T, size_t N >
void some_func( T (&t)[ N ] ) { ... }
Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • These overloads will be ambiguous, though :/ Array-to-pointer decay is given special treatment on overload resolution. It is a quagmire of suck. – R. Martinho Fernandes Jul 24 '13 at 14:06
  • Weird, [g++ 4.3.2](http://ideone.com/BqGQQR) gives an ambiguity error but [g++ 4.7.2](http://ideone.com/9miZPu) compiles (and runs) fine. And if you replace `void some_func( T *t )` with `void some_func( T *const &t )` (taking the pointer by _reference-to-const_ instead of by value) then both [g++ 4.3.2](http://ideone.com/9ZF5XV) and [g++ 4.7.2](http://ideone.com/jBUOnN) accept it... Has anybody a Clang at hand? =) – gx_ Jul 24 '13 at 14:37
  • @R.MartinhoFernandes After viewing http://channel9.msdn.com/Series/C9-Lectures-Stephan-T-Lavavej-Core-C-/Stephan-T-Lavavej-Core-Cpp-3-of-n : For the call `some_func( arr )` where `arr` is `int [3]`, _template argument deduction_ gives `void some_func( int *t ) { ... }` for the first template and `void some_func( int (&t)[ 3 ] ) { ... }` for the second one. Then _overload resolution_: the first overload requires an array-to-pointer conversion but the second one is a direct reference binding and thus should win. (At least in C++11. Maybe the rules were different in C++98/03?) – gx_ Aug 31 '13 at 20:50
  • Oh wait, now [g++ 4.8.1](http://ideone.com/6FluCK) anew gives an ambiguity error (simplified examples: http://ideone.com/DJEJ1Z , http://ideone.com/VkZHPH )... It seems that array-to-pointer decaying is considered equally good as direct reference binding, finally (at least by gcc4.8 and VS2012)... What a shame :/ – gx_ Sep 02 '13 at 12:58
2

Here I use C++11 style programming in C++03 to do SFINAE and send my arrays at one overload, and non-arrays at another:

#include <iostream>

struct false_type { enum { value = false }; };
struct true_type { enum { value = true }; };

template<typename T>
struct is_array:false_type {};
template<typename T, std::size_t N>
struct is_array<T[N]>:true_type {};

template<bool b,typename T=void>
struct enable_if {};
template<typename T>
struct enable_if<true, T> { typedef T type; };

template<typename T>
typename enable_if< is_array<T>::value>::type some_func( T& array, int unused=0 )
{
  std::cout << "array\n";
}

template<typename T>
typename enable_if< !is_array<T>::value>::type some_func( T const& not_array )
{
  std::cout << "pointer\n";
}

int main() {
  int x[3];
  some_func( x );
  int y;
  some_func( &y );
}
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524