C中的由于翻译的问题,有时候我们会混淆常量指针和指针常量的含义,但是我们都知道const char * 和char * const的区别,这里确实需要吐槽一下这两个名字的翻译。
- 常量指针(const pointer): 指针类型是Type * const,指针变量不可以被修该,但指向的对象可以被修改。
- 指针常量(pinter to const): 指针类型就是const Type * , 指针变量可以修改,但指向的对象不可以被修改。
是不是觉得上面说的是废话,这个写c的大家都知道呀,那么出一个题目:
1 | std::cout<<(typeid(const char *) == typeid(char * const))<<std::endl; |
typeid是c++的operator,用于返回表达式类型的相关信息,是c++提供的RTTI(run-time type indentification)运行时类型识别功能;
std::is_same<>是C++11的type_traits组件提供的判断两个数据类型是否相同的模版类;
你能说出输出结果吗?我敢肯定没几个人能答对的,结果是:”0 0 1 0 0 0”; 我们知道const char * , char * const, char * , 是三种不同的类型,为什么第三个输出是1呢,
这里的原因是:
Quote[link]: “In all cases, cv-qualifiers are ignored by typeid (that is, typeid(T) == typeid(const T))”
cv-qualifiers是指const和volatile限定符,那为什么const char * 的const修饰符没有被忽略呢, 这里其实就是本文要重点说明的:const char * 中的const不是修饰该指针类型的,而是修饰指针所指向的数据类型是const char类型, 所以说cv-qualifiers中的限定符是指传入类型的限定符,而不是所指类型的限定符,所以typeid(char * const) == typeid(char *), 而typeid(const char *) != typeid(char *);
问题的引入
你会在C++标准中看到很多cv-qualifiers的描述,都是上述的含义,所以这里引入之所以会写这个文章的起因:判断两个指针对应的原始type是不是相同,下面是我一开始写的第一版判断(这里简化了一下):
1 | std::cout<<std::is_same<char, std::remove_pointer<std::remove_const<const char* >::type>::type>::value<<std::endl; |
C++11的type_triats组件的remove_pointer和remove_const
先看看c++11的type_triats组件提供的remove_ponter和remove_const的功能和可能实现:
remove_pinter用于移除指针类型,获得其所指向数据的类型,它的Possible implementation:
1 | template< class T > struct remove_pointer {typedef T type;}; |
所以会有如下:
1 | remove_pointer<const char *>::type == const char |
remove_const用于移除类型的const的限定,它的Possible implementation:
1 | template< class T > |
Quote[link]: “Removing const/volatile from const volatile int * does not modify the type, because the pointer itself is neither const nor volatile.”
所以会有如下:
1 | remove_const<const char *>::type == const char* |
问题的解决
那么回到上面的问题中去,第一版输出的结果是0,原因是c++的type_traits组件的remove_const也是指cv-qualifiers,所以remove_const<const char * >后的结果还是const char *, 因为const限定符不是修饰指针的,而是修饰所指向的对象,所以正确的做法应该如下:
1 | std::cout<<std::is_same<char, std::remove_const<std::remove_pointer<const char* >::type>::type>::value<<std::endl; |
首先通过remove_pointer将const char * 类型变成const char, 然后就可以通过remove_const来是const char 变成char。有人在stackoverflow上也提过这个问题:How to remove the const from ‘char const*‘
最后
C++11的type_triats组件还是功能很强大的,它提供了一系列的类用于在编译期间获取类型信息,强烈推荐学习,它和C++很早提供的typeinfo组件,用于在RTTI运行时识别相比,更加实用,方便。
最后总结一下:
1 | const char * p1; |
p1, p2, p3是三种不同的类型变量:
- p1的const限定符是修饰其所指向的对象,即p1指向的对象为const char类型, 所以remove_pointer
结果为const char,remove_const 结果还是const char *; - p2的const限定符是修饰p2自己的,p2指向对象为char类型, 所以remove_pointer
结果为char,remove_const 结果还是char *;
参考
1. C++11的type_traits组件
2. How to remove the const from ‘char const*‘
3. typeid operator