i started reading this book c++ templates - the complete guide about templates...

in 3rd chapter {Specializations of Class Templates} it says

You can specialize a class template for certain template arguments. Similar to the overloading of function templates (see page 15), specializing class templates allows you to optimize implementations for certain types or to fix a misbehavior of certain types for an instantiation of the class template. However, if you specialize a class template, you must also specialize all member functions. Although it is possible to specialize a single member function, once you have done so, you can no longer specialize the whole class.

template<>  
class Stack<std::string> {  
  …  
};

i don't understand why use someone would want to use this specialization techinque.... to me it looks the same with an ordinary class...Whats the meaning of making a template class if you specialize it from the beggining?

Recommended Answers

All 15 Replies

Member Avatar for iamthwee

Templates is like having a single class that can handle several different datatypes meaning the code is easier to maintain, and ultimately more reusable.

I think it is also partly due to readability. Mind you I've never had the need or compulsion to use them yet.

here are two examples from the c++ standard library where specialization is used:

a. std::vector<T> is an implementation of a dynamically resizeable array; for example std::vector<double> stores a sequence of double elements. std::vector<bool> is a specialization. sizeof(T) >= sizeof(char) for any type T. to store N bool elements, the space required is N * sizeof(bool). however, we know that a bool has only two possible values (true or false) and a binary digit (bit) is sufficient to store it. the specialization std::vector<bool> optimizes on space by doing this.

b. a generic iterator type has an associated value type: the type of object in the sequence that the iterator 'points' to. it also has an associated category, which says what kind of operations are supported; is random access allowed or is it a bidirectional iterator which allows moving in both directions etc.

generic algorithms often need to have access to these associated types; an algorithm that takes a range of iterators, for example, might need to declare a temporary variable whose type is the iterators' value type. a part of generic code that needs to sort a sequence might be written efficiently using a quick sort if the iterator supports random access; otherwise it could use a bubble sort. the template class iterator_traits is a mechanism that facilitates such things.
the implementation of iterator_traits is actually very simple

template <typename Iterator> struct iterator_traits 
{
    typedef typename Iterator::iterator_category iterator_category;
    typedef typename Iterator::value_type value_type;
    typedef typename Iterator::difference_type difference_type;
    typedef typename Iterator::pointer pointer;
    typedef typename Iterator::reference reference;
};

this works because all user-defined standard library compatible iterators give this information by declaring nested types; for instance iterator I's value type, for example, would be I::value_type.

pointers are iterators too; the value type of int* is int and it allows random access to the sequence of integers in an array. however, the generalization of iterator_traits will not work for pointers; there are no declared nested types. the solution is easy; specialize iterator_traits for pointers.

template <typename T>  struct iterator_traits<T*> 
{
    typedef random_access_iterator_tag iterator_category;
    typedef T value_type;
    typedef ptrdiff_t difference_type;
    typedef T* pointer;
    typedef T& reference;
};

>>> "...allows you to optimize implementations for certain types..." eg. specialization for vector<bool>
>>> "...or to fix a misbehavior of certain types for an instantiation of the class template..." eg. specialization for iterator_traits for pointers.

here are two examples from the c++ standard library where specialization is used:

a. std::vector<T> is an implementation of a dynamically resizeable array; for example std::vector<double> stores a sequence of double elements. std::vector<bool> is a specialization. sizeof(T) >= sizeof(char) for any type T. to store N bool elements, the space required is N * sizeof(bool). however, we know that a bool has only two possible values (true or false) and a binary digit (bit) is sufficient to store it. the specialization std::vector<bool> optimizes on space by doing this.

so std::vector<bool> will occupy space: number_of_elements*1bit??

b. a generic iterator type has ...... iterator_traits for pointers.

i din't quite follow you here, but i won't ask questions now, because i am in a really entry level....i hope that i will be able to ask in a day or two, if i don't get stuck with discrete math II (i am still in the middle of exams:( )

To recapitulate, i have undesrsood the reason behind function specialization but still i don't get why would someone want to specialize a class in its definition... (i mean if it is specialized from its definition, how can it be generic...)

>so std::vector<bool> will occupy space:: number_of_elements*1bit??
No, number_of_elements * sizeof ( bool ) . The difference is that the size of bool isn't set in stone, and it's going to be at least sizeof ( char ) because char is the smallest addressable unit in C++.

[Edit: Ignore the above. I wasn't thinking when I wrote it. :P]

>i mean if it is specialized from its definition, how can it be generic...
A truly generic class is difficult to write and usually ends up being quite useless. Template specialization is simply a way to handle exceptions to the norm (varying behavior or dangerous situations) without the client code doing something completely different.

> so std::vector<bool> will occupy space:: number_of_elements*1bit??
as per c++98, yes. (the actual space would be capacity*1 bit rounded)
however, things may change in c++0x. if (and only if) that interests you, read on.

this is what the current c++98 standard specifies (iso/iec 14882 (E))
23.2.5 - Class vector<bool> [lib.vector.bool]
-1- To optimize space allocation, a specialization of vector for bool elements is provided:

namespace std
{
  template <class Allocator> class vector<bool, Allocator>
  {
    public:
      //  types:
      typedef bool const_reference;
      typedef implementation defined                iterator;  
      typedef implementation defined                const_iterator; 
      typedef implementation defined                size_type;  
      typedef implementation defined                difference_type;
      typedef bool value_type;
      typedef Allocator allocator_type;
      typedef implementation defined                pointer;
      typedef implementation defined                const_pointer
      typedef std::reverse_iterator<iterator> reverse_iterator;
      typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

      //  bit reference:
      class reference
      {
        friend class vector;
        reference();
        public:
          ~reference();
          operator bool() const;
          reference& operator=(const bool x);
          reference& operator=(const reference& x);
          void flip();              //  flips the bit
          // ...
       };
       // ....
       // rest of it is like in the generalization; elided for brevity
       // ....
   };
}

-2- reference is a class that simulates the behavior of references of a single bit in vector<bool>.

however, this is now considored by many to be a mistake. see: http://en.wikipedia.org/wiki/Boolean_datatype#C.2B.2B

there is a proposal that that the vector<bool> specialization should be deprecated in the next version of the standard. see: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2160.html

however, as per the working-draft for the c++0x standard (circulated in may 2007), this has not been done. the dynamic_bitset library proposed for C++0x (TR2) http://www.boost.org/libs/dynamic_bitset/dynamic_bitset.html#description will provide similar space optimization. and it is likely that the vector<bool> specialization would be deprecated in c++0x.

namespace std
{
  template <class Allocator> class vector<bool, Allocator>
  {
    public:
      //  types:
      typedef bool const_reference;
      typedef implementation defined                iterator;  
      typedef implementation defined                const_iterator; 
      typedef implementation defined                size_type;  
      typedef implementation defined                difference_type;
      typedef bool value_type;
      typedef Allocator allocator_type;
      typedef implementation defined                pointer;
      typedef implementation defined                const_pointer
      typedef std::reverse_iterator<iterator> reverse_iterator;
      typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

      //  bit reference:
      class reference
      {
        friend class vector;
        reference();
        public:
          ~reference();
          operator bool() const;
          reference& operator=(const bool x);
          reference& operator=(const reference& x);
          void flip();              //  flips the bit
          // ...
       };
       // ....
       // rest of it is like in the generalization; elided for brevity
       // ....
   };
}

i {think} i uderstand what you are saying but i am not sure i understand the code you provide{i will ask further details, when i learn a few more things...}

Another strange thing i read is this

std::transform (source.begin(), source.end(),  // start and end of source  
                dest.begin(),                  // start of destination  
                addValue<int,5>);              // operation

Note that there is a problem with this example: addValue<int,5> is a function template, and function templates are considered to name a set of overloaded functions (even if the set has only one member). However, according to the current standard, sets of overloaded functions cannot be used for template parameter deduction. Thus, you have to cast to the exact type of the function template argument:

std::transform (source.begin(), source.end(),  // start and end of source  
                dest.begin(),                  // start of destination  
                (int(*)(int const&)) addValue<int,5>);  // operation

what's the meaning of

a set of overloaded functions

, i 've never heard it before... {why we have this restriction}
and secondly what strange cast is this

(int(*)(int const&)) addValue<int,5>);

PS:: sorry if i am not very specific, but at this point i can't really be, cause things are a bit blur to me..

Another questionconcerning the Restrictions for Nontype Template Parameters

for beginners{like me} reading this thread

nontype template parameters may be constant integral values (including enumerations) or pointers to objects with external linkage.

so if we have this code:

template <char const* name>
class MyClass {  
  //…  
};

extern char const s[] = "hello";
extern char const *s2 = "hello";
extern char * const s3 = "why_error";


int main()
{
	
	MyClass<s> x;
	//MyClass<s2> x; //error we don't have a constant pointer
 	[B]//MyClass<s3> x; //also an error why?
[/B]
	return 0;
}

why do i get an error when i try to instantiate //MyClass<s3> x; //also an error why? thanks in advance for your time

this snippet may help you get answers:

template <const char* const name> class one {};
template <const int* const ptr> class two {};

extern const char this_is_ok[] = "no_error" ;
int this_too_is_ok = 100 ;

const char* const this_is_not_ok = this_is_ok ;
int* const this_too_is_not_ok = &this_too_is_ok ;

template< typename T, int N > void function( T (&a)[N] ) {}
// equivalent to a set of overloaded functions; for each unique combination
// of T and N, we have one overload

template< typename FN, typename T, int N > void another_function( 
                                          FN fn, T (&a) [N] ) { fn(a) ; }

int main()
{
  one< this_is_ok > fine ; //fine; address of object with external linkage.
  two< &this_too_is_ok > also_fine ; // also fine; 
                             // address of object with external linkage.
  // one< this_is_not_ok > error ; //error
  // two< this_too_is_not_ok > also_error ; //also error

  void(*pfn1)( int (&) [5] ) = &function<int,5> ; // ok
  void(*pfn2)( double (&) [8] ) = &function<double,8> ; // ok
  pfn1 = &function ; // ok, deduced
  pfn2 = &function ; // ok, deduced
  
  short array[23] ;
  function( array ) ; // ok, deduced
  
  another_function( &function<short,23>, array ) ; // ok
  
  another_function( ( void(*)(short(&)[23]) )&function, array ) ; // also ok
  // equivalent to:
  typedef short (&arg_type)[23] ; // arg_type => reference to array of 23 short
  typedef void (*fun_type) ( arg_type ) ; // fun_type => pointer 
              // to a function returning void and taking one arg of arg_type
  another_function( fun_type(&function), array ) ; // identical to earlier
                               // cast, but much more readable
  
  //another_function( &function, array ) ; // not ok; can not deduce
                               // really, can be deduced, but will not deduce
}
template <const char* const name>
class one 
{
};

template <const int* const ptr> 
class two 
{
};

///using the extern to modify the linkage
extern const char this_is_ok[] = "no_error" ;	
int this_too_is_ok = 100 ;		///it is ok because it is an integral value correct?
					///it doesn't have anything to do with the linkage, correct?
 
const char* const this_is_not_ok = this_is_ok ;
int* const this_too_is_not_ok = &this_too_is_ok ;


template< typename T, int N > void function( T (&a)[N] ) {} ///This notation is for passing be reference the pointer to the array?
// equivalent to a set of overloaded functions; for each unique combination
// of T and N, we have one overload
///oh i see, that's why the word set....when i read the book i didn't catch this detail

template< typename FN, typename T, int N > void another_function( 
		FN fn, T (&a) [N] ) { fn(a) ; }

int main()
{
	one< this_is_ok > fine ; //fine; address of object with external linkage.
	two< &this_too_is_ok > also_fine ; // also fine; 
	// address of object with external linkage.
	// one< this_is_not_ok > error ; //error
	// two< this_too_is_not_ok > also_error ; //also error

	//also what is this  int (&) [5]
	void(*pfn1)( int (&) [5] ) = &function<int,5> ; // ok
	void(*pfn2)( double (&) [8] ) = &function<double,8> ; // ok
	pfn1 = &function ; // ok, deduced
	pfn2 = &function ; // ok, deduced

	short array[23] ;
	function( array ) ; // ok, deduced //the explicit instantiation would be function<short,23>(array);


	another_function( &function<short,23>, array ) ; // ok

	another_function( ( void(*)(short(&)[23]) )&function, array ) ; // also ok
	///i don't quite get this notation  ( void(*)(short(&)[23]) )&function,
	
	///you 've lost me here....
	// equivalent to:
	typedef short (&arg_type)[23] ; // arg_type => reference to array of 23 short
	typedef void (*fun_type) ( arg_type ) ; // fun_type => pointer 
	// to a function returning void and taking one arg of arg_type
	another_function( fun_type(&function), array ) ; // identical to earlier
	// cast, but much more readable

	//another_function( &function, array ) ; // not ok; can not deduce
	// really, can be deduced, but will not deduce
}

i've modified the code, to be more readable for me.
My comments are marked with "///"

thanks vijayan for your help, :)

// ...
// ...
///using the extern to modify the linkage
// *** correct
extern const char this_is_ok[] = "no_error" ;

int this_too_is_ok = 100 ;    ///it is ok because it is an integral value correct?
// *** not correct
///it doesn't have anything to do with the linkage, correct?
// *** not correct
// *** the default linkage for modifiable values is external.
// *** this_too_is_ok cannot be used as a template parameter; 
// ***                    it is not a constant known at compile time.
// *** &this_too_is_ok can be used as a template parameter; 
// ***       it is the address of an object with external linkage.
// *** compare with:
// *** const int NUM = 7 ; // constants have internal linkage by default.
// *** NUM can be used as a template parameter; constant known at compile time
// *** &NUM cannot be used as a template parameter; NUM has internal linkage
// *** extern const int VAL = 78 ; // force external linkage on VAL.
// *** VAL can be used as a template parameter; constant known at compile time
// *** &VAL can also be used as a template parameter; VAL has external linkage


template< typename T, int N > void function( T (&a)[N] ) {} ///This notation is for passing be reference the pointer to the array?
// *** in T(&a)[N] 'a' is a reference to the array
// *** typedef T(*ptr_array_type)[N] ; // type 'ptr_array_type' is a pointer to the array
// *** ptr_array_type& is a reference to the pointer to the array

int main()
{
  //
  short array[23] ;
  function( array ) ; // ok, deduced //the explicit instantiation would be function<short,23>(array);
  // *** correct


  another_function( &function<short,23>, array ) ; // ok

  another_function( ( void(*)(short(&)[23]) )&function, array ) ; // also ok
  ///i don't quite get this notation  ( void(*)(short(&)[23]) )&function,
  ///you 've lost me here....
  // *** typedef short(&reference_to_array_type)[23] ;
  // *** reference_to_array_type => reference to array of 23 short values
  // *** typedef void (*function_type)(reference_to_array_type) ;
  // *** function_type => pointer to function returning void and taking one
  // ***                  arg of type reference to array of 23 short values
  // *** &function => address of function<T,N> ; ambiguous: what T, what N?
  // *** (function_type)&function => address of function<short,23>
  // *** function_type(&function) => same thing
  // *** ( void(*)(reference_to_array_type) )&function => same thing
  // *** ( void(*)( short(&)[23] ) )&function => same thing
  // ...
}
commented: thanks for helping! +1
// *** const int NUM = 7 ; // constants have internal linkage by default.
// *** NUM can be used as a template parameter; constant known at compile time
// *** &NUM cannot be used as a template parameter; NUM has internal linkage
// *** extern const int VAL = 78 ; // force external linkage on VAL.
// *** VAL can be used as a template parameter; constant known at compile time
// *** &VAL can also be used as a template parameter; VAL has external linkage

How can an identifier have external linkage and his address to be internal?

template< typename T, int N > void function( T (&a)[N] ) {} 
///This notation is for passing be reference the pointer to the array?
-->>what i wrote makes no sense...
// *** in T(&a)[N] 'a' is a reference to the array
-->>why you pass a reference to an array, since the array is a pointer by itself?
another_function( ( void(*)(short(&)[23]) )&function, array ) ; // also ok
  ///i don't quite get this notation  ( void(*)(short(&)[23]) )&function,
  ///you 've lost me here....
  // *** typedef short(&reference_to_array_type)[23] ;		
  // *** reference_to_array_type => reference to array of 23 short values
  // *** typedef void (*function_type)(reference_to_array_type) ;
  // *** function_type => pointer to function returning void and taking one
  // ***                  arg of type reference to array of 23 short values
  
  // *** &function => address of function<T,N> ; ambiguous: what T, what N?
  // *** (function_type)&function => address of function<short,23>
  // *** function_type(&function) => same thing
  // *** ( void(*)(reference_to_array_type) )&function => same thing
  // *** ( void(*)( short(&)[23] ) )&function => same thing
  // ...
}

great explanation!

so in the end those two:

another_function( &function<short,23>, array ) ; 

another_function( ( void(*)(short(&)[23]) )&function, array ) ;

are equivalent.... the first one is a lot more readable!

///> how can an identifier have external linkage and his address to be internal?

extern int an_identifier ;
// declares an_identifier as an int with external linkage
// it may be defined in some other translation unit

void foo()
{
  an_identifier = 23 ;
  // the code generated by the compiler is (logically)
  // move the value 23 to some sizeof(int) bytes at some address in memory
  // mov DWORD PTR address_of_variable_an_identifier, 23 ; (i386)
  // the compiler does not know what address_of_variable_an_identifier is
  // so it generates the code for the mov instruction without specifying
  // the address_of_variable_an_identifier eg.
  // opcode_for_mov_an_int_size_constant_to_an_address ???????? 23
  // and issues a FIXUP request to the linker:
  // replace ???????? here with the address of the identifier 'an_identifier'
  // for the linker to be able to do the fixup, it must be able to 
  // figure out (resolve) the address of the identifier 'an_identifier'
  // for this, an_identifier must be defined somewhere with external linkage.
  // if it is not defined (with external linkage) the linker gives an error:
  // *** unresolved external symbol 'an_identifier'
  // if it is defined more than once (with external linkage), the error is:
  // *** symbol 'an_identifier' is multiply defined
  // note: names in c++ are decorated (mangled); so the name the linker sees 
  //       would be the decorated name of 'an_identifier'
  // so external linkage means:
  //      the identifier is visible outside the translation unit
  //      this implies that a name to address mapping for this identifier
  //      is available throughout the program
}

// however, if we write
int* const an_address = &an_identifier ;
// the identifier 'an_address' has internal linkage

extern char * const s3 = "why_error";
// 's3' is a pointer and has external linkage; contains the the address
// of the first element the anonymous array "why_error"
// the array itsef is not visible outside this translation unit

extern const char no_error[] = "why_no_error";
// no_error is an array and has external linkage; when used in an
// expression, the identifier 'no_error' decays to a pointer.


///> why you pass a reference to an array, since the array is a pointer by itself?
void foo( int array[] )
{
  // here array is a pointer, the address of the first element
  // we have no idea how many elements are there in this array
  // need to pass it sepearately if we need that information
}
enum { SIZE = 50 };
void bar( int (&array)[SIZE] )
{
  // here array is a reference, refers to an array of SIZE int values
  // we know how many elements are there in this array
  // however, to be able to use the reference, the number of
  // elements must be a constant known at compile time.
  // if it is so, passing a reference is clearly better than 
  // passing a pointer to the first element and passing the size;
  // more readable, easier, less error prone etc.
  // if the number of elements is not known at compile time,
  // a c++ programmer would use a vector<>, not an array
}

// note: ...since the array is a pointer by itself?
// the array is not a pointer, it's name 'decays' to a pointer
// to the it's first element. eg. to allocate a two-dimensional
// array dynamically, we would to write
extern int M ;
enum { N = 100 } ;
double(*array2d)[N] = new double[M][N] ;
// array2d is a pointer to the first element of the allocated array
// the first element is an array of N integers; so the type of
// array2d is 'pointer to an array of N integers'
// N must be a constant known at compile time, M need not be one.
// again, if N is not a constant known at compile time,
// a c++ programmer would use a vector<>
///> how can an identifier have external linkage and his address to be internal?
//--->>no i see that this question didn't make sense

extern int an_identifier ;
// declares an_identifier as an int with external linkage
// it may be defined in some other translation unit

void foo()
{
  an_identifier = 23 ;
  // the code generated by the compiler is (logically)
  // move the value 23 to some sizeof(int) bytes at some address in memory
  // mov DWORD PTR address_of_variable_an_identifier, 23 ; (i386)
  // the compiler does not know what address_of_variable_an_identifier is
  // so it generates the code for the mov instruction without specifying
  // the address_of_variable_an_identifier eg.
  // opcode_for_mov_an_int_size_constant_to_an_address ???????? 23
.....
  // so external linkage means:
  //      the identifier is visible outside the translation unit
  //      this implies that a name to address mapping for this identifier
  //      is available throughout the program
}

I think i undestand, here is what i have in my mind:

To use something with templates we must know its value in compile time. int this_too_is_ok = 100 ; where we can use &this_too_is_ok because its value {which is the address of this_too_is_ok} is known at compile time, since the default linkage for modifiable values is external... const int NUM = 7 ; we know it's value at compile time{since it is a constant} so we can use NUM as a template parameter.But we can't use &NUM because constants have default internal linkage so its address isn't specified until linking time... extern const int NUM = 7 ; no it has external linkage so {according to the procedure you posted above} its address is known at compile time...

extern char * const s3 = "why_error";
// 's3' is a pointer and has external linkage; contains the the address
// of the first element the anonymous array "why_error"
// the array itsef is not visible outside this translation unit

extern const char no_error[] = "why_no_error";
// no_error is an array and has external linkage; when used in an
// expression, the identifier 'no_error' decays to a pointer.

with the second method the array "why_no_error" is visible outside the translation unit?

///> why you pass a reference to an array, since the array is a pointer by itself?
void foo( int array[] )
{
  // here array is a pointer, the address of the first element
  // we have no idea how many elements are there in this array
  // need to pass it sepearately if we need that information
}
enum { SIZE = 50 };
void bar( int (&array)[SIZE] )
{
  // here array is a reference, refers to an array of SIZE int values
  // we know how many elements are there in this array
  // however, to be able to use the reference, the number of
  // elements must be a constant known at compile time.
  // if it is so, passing a reference is clearly better than 
  // passing a pointer to the first element and passing the size;
  // more readable, easier, less error prone etc.
  // if the number of elements is not known at compile time,
  // a c++ programmer would use a vector<>, not an array
}

yes, but couldn't you write

void foo( int array[50] )
{
//....
}

doesn't this has the same effect?

///> To use something with templates we must know its value in compile time.
// *** also, it must a constant which can not be modified at run time.
// *** types are always known at compile time (and at compile time alone)
// *** for addresses, the FIXUP required must be known at compile time
// ***    the value must be known at link time and is a constant that
// ***    can not change fron one translation unit to another;
// ***    therefore, requires external linkage.

extern const char no_error[] = "why_no_error";
///> with the second method the array "why_no_error" is visible outside the translation unit?
// *** yes (to be precise, no_error[] is the array visible outside, 
// *** "why_no_error" is only it's (anonymous) initializer)

///> yes, but couldn't you write ...
void foo( int array[50] ) { /* ... */ }
///> ...doesn't this has the same effect?
// *** 50 here is just a place-holder, it's value is ignored.
// *** this is the same as writing
void foo( int* array ) ;
// *** or
void foo( int array[] ) ;
// *** or
void foo( int array[1000000] ) ;
// *** type of array is an int* in all these declarations
// *** all declare the same function 'foo'
// for example,

#include <typeinfo>
#include <cassert>
#include <cstddef>

std::size_t fun_one( int arg1[1000], int arg2[99999] )
{ assert( sizeof(arg1)==sizeof(arg2) ) ; return sizeof(arg1)+sizeof(arg2) ; }

std::size_t fun_two( int arg1[20], int arg2[] )
{ assert( sizeof(arg1)==sizeof(arg2) ) ; return sizeof(arg1)+sizeof(arg2) ; }

std::size_t fun_three( int arg1[], int* arg2 )
{ assert( sizeof(arg1)==sizeof(arg2) ) ; return sizeof(arg1)+sizeof(arg2) ; }

std::size_t fun_four( int* arg1, int arg2[500] )
{ assert( sizeof(arg1)==sizeof(arg2) ) ; return sizeof(arg1)+sizeof(arg2) ; }

// *** 1000 and 99999 are not mere place holders; their values have meaning 
std::size_t fun_five( int (&arg1)[1000], int (&arg2)[99999] )
{
   assert( sizeof(arg1) == sizeof(int) * 1000 ) ;
   assert( sizeof(arg2) == sizeof(int) * 99999 ) ;
   return sizeof(arg1) + sizeof(arg2) ;
}

// *** 1000 is not a mere place holder; it's value has meaning
std::size_t fun_six( int (&arg1)[1000], int arg2[99999] )
{
   assert( sizeof(arg1) == sizeof(int) * 1000 ) ;
   assert( sizeof(arg2) == sizeof(int*) ) ;
   return sizeof(arg1) + sizeof(arg2) ;
}

int main()
{
  assert( typeid(fun_one) == typeid(fun_two) ) ;
  assert( typeid(fun_two) == typeid(fun_three) ) ;
  assert( typeid(fun_three) == typeid(fun_four) ) ;
  assert( typeid(fun_one) != typeid(fun_five) ) ;
  assert( typeid(fun_five) != typeid(fun_six) ) ;
  
  std::size_t (*pfn1) ( int*, int* ) = &fun_one ; // ok
  std::size_t (*pfn2) ( int*, int* ) = &fun_two ; // ok
  std::size_t (*pfn3) ( int[], int[] ) = &fun_three ; // ok
  std::size_t (*pfn4) ( int[100], int[500] ) = &fun_four ; // ok
  std::size_t (*pfn5) ( int[1], int[1] ) = &fun_one ; // ok
  pfn1 = pfn2 = pfn3 = pfn4 = pfn5 = pfn1 ; // all are of the same type

  int i=0, j=7 ;
  int a[1000] = {0}, b[99999] = {0} ;
  assert( fun_one( a, b ) == 2 * sizeof(int*) ) ;
  assert( fun_one( &i, &j ) == fun_one( a, b ) ) ;
  assert( fun_two( &i, a ) == fun_three( b, &j ) ) ;
  assert( fun_three( &i, &j ) == fun_four( a, b ) ) ;
  assert( fun_one( &i, &j ) == fun_four( a, b ) ) ;
  assert( fun_five( a, b ) == sizeof(a) + sizeof(b) ) ;
  
  assert( fun_five( a, b ) == sizeof(int) * (1000+99999) ) ;
  assert( fun_six( a, a ) == sizeof(a) + sizeof(int*) ) ;
  fun_six( a, b+250 ) ; // ok; b decays into a pointer
  // fun_six( b, a ) ; // error; b is not an array of 1000 ints
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.