Q:

c++ template C-string parameter

I was working on a template for a function. To simplify, say it looks like this:
template < typename T >
void f(const T & x)
{
    cout << "generic case" << endl;
    cout << x << endl;
}

I always thought that C-strings cannot be used as template parameter arguments. But the following actually works (using g++ 4.5.1):
f("hello world");

So my question is: what is T when I call f("hello world")? 
I tried to specialize to see what exactly is happening. For instance since char[] is const char* I looked at this (which obviously does not work):
template < typename T >
void f(const T & x)
{
    cout << "generic case" << endl;
    cout << x << endl;
}
template <>
void f(const const char * T & x)
{
    cout << "char[] case" << endl;
    cout << x << endl;
}

and tried several variations. None of them work.
ASIDE: I don't really need this for what I'm doing. I need a specialization for the case of T = "C-string" so I just wrote another template function:
template < typename T >
void f(const T & x)
{
    cout << "generic case" << endl;
    cout << x << endl;
}
template < typename T >
void f(T x[])
{
    cout << "T[] case" << endl;
    cout << x << endl;
}

I'm just asking because I'm curious as to what exactly is happening and why is a C-string allowed to be a template argument when what I've read said that it can't be. I must have misread/misunderstood something about templates.

A:

There is no C-string type.  The term C-string defines the content, not the type.  It refers to a part of a character array that has a null character somewhere in it, which is interpreted by some functions to mean the end of the string.
What you are really interested in though, is a string literal.  A string literal has type const char[N], where N is the number of characters in the string, including the implicit null terminator.  So "hello world" is of type const char[12].  You can specialize for it like this:
template<>
void f(const char(&x)[12])
{
   cout << "const char[12] case" << endl;
   cout << x << endl;
}

Note that this only covers arrays of size 12.  However, you can overload, (not specialize) f() for all sizes like this:
template<size_t N>
void f(const char(&x)[N])
{
    cout << "const char[" << N << "] case" << endl;
    cout << x << endl;
}

Also note that these methods will cover normal named arrays too.  There is no way to distinguish between them and string literals.

