Help: How many explicit specializations required?

From: CoolPint (coolpint_at_yahoo.co.uk)
Date: 09/16/04


Date: 15 Sep 2004 23:49:53 -0700

I would be grateful if someone could point out if I am understanding
correctly and suggest ways to improve. Sorry for the long message and
I hope you will kindly bear with it. I have to make it elaborate to
make sure my questions are clear enough.

Let's say I need to write a function whose logic is same for all types
(T) except in the case of T * (including const T *). Furtheremore ,
the function needs to be written differently for character
strings(C-style).

I have tried various combination of explicit specialization and
overloading and came to the following conclusion:

1. I need to have a template for T
2. I need to overload the template for T *
3. I need to have explicit specialization or a normal function for
char *
4. I need to have explicit specialization or a normal function for
const char *

I tried to make 3 & 4 into one (hoping const char * will accept char
*), but I learned it cannot be when 1 or 2 is present since char *
will be instantiated from the template.

My questions are:

A. Since their logic will be exactly same, is there a way to make 3
and 4 into one function?

B. Passing arrays of T will be resolved into case 2. What if I don't
wish to treat arrays of T as pointers to T? Let's say the logic of
working on pointers to T will give unexpected result when worked on
arrays of T. Is there any way to treat arrays of T differently from
pointers to T?

C. The primary template passes by value. Can I make it
pass-by-const-reference?
   Actually, I made my attempt at this and needs to know if I am doing
right.

Here is a simple program I wrote to help me understand what's going
on:

#include <iostream>
using std::cout;

template <typename T>
void func(T)
{ cout << "primary\n" ; }

template <typename T>
void func(T *) // should handle T * as well as const T *, I think
{ cout << "Overloading for Pointers\n"; }

template <>
void func(char *)
// BTW, Writing above as func<char *> makes gcc behave differently
{ cout << "specialization: char * \n"; }

template <>
void func(const char *)
// BTW, Writing above as func<const char *> makes gcc behave
differently
{ cout << "specialization: const char * \n"; }

int main()
{
  char * pc = "whatever";
  const char * pcc = "whatever";
  char ac[] = "whatever";
  const char acc[] = "whatever";
  int i;
  const int ci=1;
  int ai[] = { 0 };
  const int aci[] = {0};

  cout << "int: "; func(i);
  cout << "int *: "; func(&i);
  cout << "const int: "; func(ci);
  cout << "const int *: "; func(&ci);
  cout << "int [] : "; func(ai);
  cout << "const int [] :"; func(aci);

  cout << "Literal String: "; func("whatever");
  cout << "char *: "; func(pc);
  cout << "const char *: "; func(pcc);
  cout << "char []: "; func(ac);
  cout << "const char[]: "; func(acc);
}

As asked in question C, can I make the primary function template pass
by const-reference to T? Here is my attempt but it looks rather
horrible as I need to provide the four different explicit
specializations whose internal logics are exactly same for handling
C-style strings. And I think this would also create code bloat as
different instantiations will be made for array of char of different
sizes.

Please have a look and give me some advice if I can make those four
separate explicit specialization combined into one (maybe two?)

#include <iostream>
using std::cout;

template <typename T>
void func(const T &)
{ cout << "primary\n" ; }

template <typename T>
void func(T * const &)
{ cout << "Overloading for Pointers\n"; }

template <>
void func(char * const &)
{ cout << "specialization: char * \n"; }

template <>
void func(const char * const &)
{ cout << "specialization: const char * \n"; }

template <int N>
void func( const char (&) [N] )
{ cout << "Overloading for const char[] \n"; }

template <int N>
void func( char (&) [N] )
{ cout << "Overloading for char[] \n"; }

/*
template <typename T, int N>
void func( T (&) [N] )
{ cout << "Overloading for T [] \n"; }
*/

// Including this creates ambiguity for arrays of T
// as it collides with the primary one.

int main()
{
  char * pc = "whatever";
  const char * pcc = "whatever";
  char ac[] = "whatever";
  const char acc[] = "whatever";
  int i;
  const int ci=1;
  int ai[] = { 0 };
  const int aci[] = {0};

  cout << "int: "; func(i);
  cout << "int *: "; func(&i);
  cout << "const int: "; func(ci);
  cout << "const int *: "; func(&ci);
  cout << "int [] : "; func(ai);
  cout << "const int [] :"; func(aci);

  cout << "Literal String: "; func("whatever");
  cout << "char *: "; func(pc);
  cout << "const char *: "; func(pcc);
  cout << "char []: "; func(ac);
  cout << "const char []: "; func(acc);
}

Question B.2: Now, arrays of T are resolved into primary template. Is
there any way to differentiate between T and array of T?

Thank you for your patience, time and kind advice in advance.



Relevant Pages