无论是类模板还是函数模板,对其模板参数类型都隐含一定要求。比如:
但是,所有这些要求都仅仅是不成文的约定。无论是容器所存类型,还是算法的迭代器类型,都可以归为模板参数的最主要一类即类型模板参数之中。但是,在模板参数列表中,类型模板参数仅仅是用typename
标识以区别于其他参数类型。
语法上,类型模板参数的“值”可以是任何类型。仅从模板参数定义上,编译器无从得知模板对其参数有何种要求。当用户用集合容器保存一个无法进行<
基表的类型时,编译器无法在第一时间察觉到所用类型与对模板参数要求不匹配,只有在编译到容器或者算法内部的某个对模板参数有要求的实际语句时,才会发现这一失配。通常算法或容器的内部实现会有多重模板函数调用,编译器有可能编译到很深层次的调用时才会出错。如此,一个最直接的后果就是一长串晦涩难懂的出错信息。
对比函数参数类型必须是预定义好的类从而可对参数值做出详细的约束,模板参数仅仅用typename标识就非常简陋了。要想让编译器尽早察觉模板实参不满足要求,就需要一种详细描述模板对其参数的种种要求。但是C++标准中并没有考虑到这一描述需求,从而使得编译模板时无法尽早发现模板参数值不满足要求的要求,导致模板的编译出错信息冗长模糊。
为此,有人提出C++语法中需要增加对模板参数类型的要求规范:概念
概念将模板对其参数的要求从原本语法外的约定变为正式的语法描述,从而使编译器能知晓该要求,并可以在第一时间检测赋予模板参数的类型是否符号要求。一旦发现不符合概念,编译器就可立即报错并明确指出错误所在,而不用等到最后编译无法继续进行再报错
看个例子:std::min算法采用<
运算符比较两个参数值,所以要求比较数据类型必须是“可进行小于运算”的。按照概念的提案,“可进行<运算”这一要求可以成文的描述成如下形式:
// 定义一个概念 concept LessThanComparable<typename T> { bool operator < (T, T); }
“概念”的定义体以concept
开头,后接概念的名字以及所约束的类型参数。在概念定义体中,要求类型T必须有对应的“<”操作符函数,但不关系也不比声明该操作符函数的具体实现方法。
此概念定义后,定义std::min
函数模板时,就可以明确约束模板参数必须时“可进行<运算”的,按照题案器代码可以改写为:
template<LessThanComparable T> T const& min(T const &left, T const& right){ if(left < right){ return left; } return right; }
原本仅用typename或者class修饰的模板参数T,改用之前所定义的LessThanComparable 约束后,编译器就知道该函数模板对参数的要求。一旦不符合这个要求,编译期就可以立马准确的报错