概念(concept)
使用模板进行通用编程的关键思想是定义能通过各种类型(type)使用的函数和类。但是,在实例化模板时经常会出现用错类型的问题,其结果通常是几页难懂的报错信息。
现在概念来了,这个问题可以休矣。概念让你能为模板编写要求,而编译器则可以检查这个要求。概念革新了我们思考和编写通用代码的方式。原因如下:
- 模板的要求是接口的一部分;
- 类模板中的函数重载或特殊化可以基于概念进行;
- 因为编译器能够比较模板参数的要求与实际的模板参数,所以能得到更好的报错信息。
但是,这还不是全部。
- 你可以使用预定义的概念,也可以定义你自己的概念;
- auto 和概念的用法统一到了一起。你可以不使用 auto,而是使用概念;
- 如果一个函数声明使用了一个概念,那么它会自动变成一个函数模板。由此,编写函数模板就变得与编写函数一样简单。
下面的代码片段展示了一个简单概念 Integral 的定义和使用方式:
template<typename T> concept bool Integral(){ return std::is_integral<T>::value; } Integral auto gcd(Integral auto a, Integral auto b){ if( b == 0 ) return a; else return gcd(b, a % b); }
Integral 这个概念需要 std::is_integral<T>::value 中的类型参数 T。std::is_integral<T>::value 这个函数来自 type-traits 库,它能在 T 为整数检查编译时间。如果 std::is_integral<T>::value 的值为 true,则没有问题。如果不为 true,则你会收到一个编译时间报错。如果你很好奇(你也应该好奇),我的这篇文章介绍了 type-traits 库:https://www.modernescpp.com/index.php/tag/type-traits。
gcd 算法是基于欧几里德算法确定最大公约数(greatest common divisor)。我使用了这个缩写函数模板句法来定义 gcd。gcd 要求其参数和返回类型支持概念 Integral。gcd 是一类对参数和返回值都有要求的函数模板。当我删除这个句法糖(syntactic sugar)时,也许你能看到 gcd 的真正本质。
下面这段代码在语义上与 gcd 算法等效:
template<typename T> requires Integral<T>() T gcd(T a, T b){ if( b == 0 ) return a; else return gcd(b, a % b); }
概念(concepts)就是一种编译时谓词,指出一个或多个类型应如何使用1
概念(concepts)提供基础语言概念的定义,它们能用于进行模板实参的编译时校验,以及基于类型属性的函数派发。这些概念在程序中提供等式推理的基础。2
通过使用concepts,传统的模板元编程方面关于编译错误的痛点可以得到极大改善,编译器可以给出更加符合人类直觉的错误提示。3
概念是用于表达通用算法对其模板参数的期望的谓词。
概念允许您正式记录模板上的约束,并让编译器强制执行。另外,您还可以利用这种强制执行功能,通过基于概念的重载来缩短程序的编译时间。4
concepts标准库中的方法与type_trais很像。解决的问题与SFINAE5接近。
————————————————
longji