
c++类型的多样性让人抓狂,很多非常压根没见过的类型.但是他却会用上,比如,很多人不知道nullptr他的本质也是一个常量,所以他也有类型,其实他的类型是nullptr_t. 这里就需要稍微科普一下了.
nullptr本来是没有类型的,但是在开发标准库的时候需要开发一种智能的指针.比如常见的智能指针,shared_ptr,若我们想要让他普通指针一样可以进行比较,如下
std这些情况下,我们则不得不要知道nullptr的类型以方便我们使用进行运算符重载.所以在c++11中,c++加入了nullptr的类型定义,如下
#include回到正题,c++如此多样的类型,我们若要进行模版编程会变得非常被动,我们永远不知道会输入的是什么样的类型,若我们没有输入正确的类型,可能直接导致编译不通过.举个简单的例子,假如我们这样写一个类,
template编译结果
temp.cpp: In function ‘int main(int, char**)’:
temp.cpp:348:17: error: use of deleted function ‘test<int&>::test()’
test<int &> aref;
^~~~
temp.cpp:169:7: note: ‘test<int&>::test()’ is implicitly deleted because the default definition would be ill-formed:
class test{
^~~~
temp.cpp:169:7: error: uninitialized reference member in ‘class test<int&>’
temp.cpp:171:7: note: ‘int& test<int&>::a’ should be initialized
T a;简而言之则是,因为a是一个引用类型,所以你需要在构造的时候进行初始化.这种错误是非常不爽的,所以当我们写一个并非给引用来使用的模板类,需要做的是要是
- 在编译期间禁止使用引用
- 容忍使用引用,但是却偷偷把引用去掉,仅仅保留原本的类型.
虽然上面的建议写着是引用,但是同时对const,volatile,nulllptr_t,右值等等类型通用.甚至为了写出一个完善的类,我们甚至需要同时对他们进行同样的处理.并对所有模板类进行同样的处理.
第一种处理,编译期间禁止使用,有两种方式,第一种是使用特例化,第二种是使用enable.第一种可能编译期间提示信息比较友好,而第二种方式则是代码量少.
特例化的核心点则是,未定义的模板类型是不会被编译通过.若你仅仅声明某个特例化而未定义,编译器会提示某个XXX类型未定义,这样的信息非常直白明了.回到刚刚的test类,我们仅声明一个引用的特例化模板,编译器就能明白我们的需求了.
template编译结果
temp简而言之是,不能识别该类型,所以变量不能被定义.同理,这样的做法还可以用于下面的形式
template若你不想写可以参照下面的使用方式,先定义一个头文件,illegal.h
#ifdef ClassName
然后在对应的类后面#define相应的名字和#include该头文件
//test 的定义
第二种做法是用标准库中enableif类型函数,enable_if的核心也很简单,把不能用的类型信息打包成一个类型函数,然后判断输入的类型是否符合要求.在c++11开始,在#include<type_traits>有着一大堆可以用来判断类型信息的类型函数.比如,is_reference,用来判断类型是否为引用.其中也有一个类型函数叫做enable_if,他的大致定义如下:
#include它的参数里面有一个bool值,若B为false,对应该行的则会编译不通过.所以道理到这里大家也就明白了,其实这就是一个很简单的布尔运算表达式.对此,在test的定义中,我们可以这样写.
template编译结果如下
temp这样的信息比较模糊,但是却是可以减少代码量的一种方式,写代码的时候比较直观的一种方式.当然上面的写法可以用另外一种方式来进行简化,比如使用enable_if_t和使用is_reference_v(c++17),这样可能更直观.
有了上面的介绍,我们就可以直接来进行封装了.可以先来封装一个判断合法类型的类型函数.
template这样子相比第一种办法则是写代码的时候比较直观,不需要用到宏等c语言里面的手段.劣势则是编译错误的信息提示比较模糊,需要自己的琢磨.当然若你细致琢磨,你也会发现上述两种办法也并不完善,不单单提示信息,甚至还会影响到后面扩展的方式,比如说
第一种办法,若你的类突然想要支持引用的写法.你要做的很简单,把那个特例化的声明写出来,能让编译器能找到即可.
第二种办法,若你想扩展,则要考虑布尔运算自己把enable_if_t<bool>参数中的is_legal_v<T>替换成is_legal_v<T>||std::is_reference<T>::value,切忌不要修改is_legal_v,否则会让其他也使用的类也会跟着一起被动变化.
关于后面不编译期间报错的办法,也有其的道理所在,以后有机会也会写吧.以后再说吧.
end...