C++ string literals

从一个例子谈起,下面的例子摘自《C++ Templates》。
[cpp]

include

include

template
void ref (T const& x)
{
std::cout << "x in ref(T const&): "
<< typeid(x).name() << 'n';
}

template
void nonref (T x)
{
std::cout << "x in nonref(T): "
<< typeid(x).name() << 'n';
}

int main()
{
ref("hello");
nonref("hello");
}
[/cpp]

用g++编译,得到运行结果如下:

x in ref(T const&): A6_c
x in nonref(T): PKc

(注:A6_c表示:array(A) of 6 chars (c);PKc表示:pointer (P) to const (K) char (c)。)

初看到这个例子我有些惊讶,为什么同样是字符串常量,通过引用传递和非引用传递差别就这么大呢?其实真想想也没什么大惊小怪的。看下面的分析。

首先,对于字符串常量,在C++标准里的类型已经发生了变化,以前是char [],现在是const char[],参考C++06 2.13.4:

An ordinary string literal has type “array of n const char” and static storage duration,
where n is the size of the string as defined below, and is initialized with the given characters.

但为了保持兼容,一部分当char []来使用的代码仍然有效。这不像C99,C99照顾兼容照顾得更厉害,所以这点仍然没变,它只说了一句:

If the program attempts to modify such an array, the behavior is undefined.

有些跑题了……咳,所以,当引用传递时自然是原来的类型const char[]。

而直接传递呢?直接传递其实也是const char[]!我没开玩笑!想想啊,它的类型怎么能会突然就变呢?不可能!问题是为什么typeid看到的是const char *?很简单啊,因为这是函数!当数组作为函数参数传递时会退化为指针,我们耳熟能详的这句话在C++里也适用啊!所以,这里拧把的不是模板,而是引用,别被模板给吓怕了!

如果你仔细看看原书那一节,你会发现还有个问题比较拧把:不同长度的同类型数组其实也不是一个类型,这也不难理解,放C里看看你就知道了:C里的一切类型皆是连续的内存块,数组更是,不同长度的同类型数组长度还是不一样啊,怎么能是同一个类型呢?所以这就有问题了,不同长度的字符串常量还不能作为同一个类型直接传递进模板了?如果我们用引用传递的话,是,可如果不用的话就行了,这正是解决方法!或者更干脆,直接用std::string。