转眼间,C++20的标准已经发布快两年了。不少C++的开源项目也已经将标准升级到最新的C++20了,笔者也开启了新标准的学习历程了。所以借这系列的博文,记录下笔者学习新标准的一些心得与吐槽~~1.First Look 先从一个群友的一个实际的问题出发,我们来看看
作为C++20系列的第一篇开篇之文,就要从千呼万唤始处理的concept
聊起了,后续很多新的feature的实现,也仰赖新的concept
的实现,后续笔者的文章也会逐步展开。OK,开始我们C++20旅程的第一站:concept
concept
可以解决什么问题。是怎么样通过coding实现的。文章图片
- SFINAE
熟悉C++模板编程的小伙伴肯定第一时间想到通过SFINAE的方式来解决,让笔者来解决这个问题的话,会写出下面的代码:
template
T test(T a) {
static_assert(!std::is_same_v);
static_assert(!std::is_same_v);
static_assert(!std::is_same_v);
static_assert(!std::is_same_v);
return a;
}
这里写的代码是一个略微Trick的表达,利用
decltype
去获取操作符计算后的类型,然后用std::is_same_v
进行一个其实没什么意义的类型比较,来满足static_assert
的语义,最终满足我们对模板类型T
的一些约束。- concept
显然上面的方式是很不直观的,虽然能达到咱们的目的,但是从代码优雅角度来说是一种较差的选择实现。
template
concept Cal = requires (T a) {
a + a;
a - a;
a * a;
a / a;
};
template
requires Cal
T test(T a) {
return a;
}
这是通过
concept
来实现的一个类型约束方式,Cal
代表着一个concept的实现,requires
中花括号的内容就代表了对于类型T
的约束,要满足下面的操作符a + a;
a - a;
a * a;
a / a;
Bingo! 似乎C++20给了我们一个更好的trait,接着往下看,我们继续来细探Concept的实现。
2. How to use
- concept的定义
template
concept Cal = requires (T a) {
a + a;
typename T::type;
{a + a} -> std::same_as;
requireConcept2;
};
这里定义了4个requires的要求,只有满足这4个条件才能通过
concept
的限制,正常进行编译。1).
a + a
这个是最简单的,该表达式能通过编译则代表符合要求,这里不会进行实际的计算。2).
typename T::type
代表需要,T
类型定义了type
类型,才符合要求3).
{a + a} -> std::same_as
这里的{}
代表了decltype(a + a)
之后的类型需要满足后面的concept
的需求。这是一个语法糖,也可以通过这样来实现:requires std::is_floating_point_v
4). 最后的
require Concept2
这代表了concept
的嵌套逻辑。requires
后面可以带任意的concept
- concept的使用
concept
定义之后,我们就可以利用concept
来进行模板类型的约束了。这里“回字有四种写法”,大家可以选择自己喜欢的方式来使用。(真搞不懂搞这么多写法干什么,不能统一一下吗?, 下面介绍的顺序随笔者的偏好以此递减)
template
requires Cal
T test(T a)
{
return a + a;
}
1). 这是笔者最认可的一种书写方式,语义明确,在模板类型定义之后明确对它的要求。
template
T test(T a)
{
return a + a;
}
2). 也还行吧,模板参数前指定了
concept
,也比较明确直观。Cal test(Cal a)
{
return a + a;
}
3).
concept
命名一长就显得有些琐碎了,并且把concept
当类型使用了,看起来有些怪异了。template
T test(T a) requires Cal
{
return a + a;
}
4). 把
concept
写后面的都是异端,当然如果你喜欢的话,也ok。3. concept的本质 concept本质上是:一个可以套用在模板上的
constexpr bool
值。相信下面这段代码能帮助你更好的理解它:
template
concept Con = std::is_floating_point_v;
int main(int argc, char* argv[]) {
static_assert(std::is_same_v), bool>);
std::cout << Con << std::endl;
return 0;
}
显然,上面的代码我们用
is_same_v
确定了concept
生成的类型就是bool
类型。而同样的,在运行期,咱们也可以将concept
的结果作为一个bool
常量进行使用,并打印。所以,take it easy。
concept
很简单,它只是C++20给你提供的一个better的工具,来摆脱被疯狂的模板报错所支配的恐惧。但即使你完全不了解它,使用老的方式,依然能够同样解决问题。4.小结 C++的一些模板推断的错误常常让人抓狂。而很多时候我们使用它需要
- 要进行模板推断类型的编程设计
- 利用SFINAE的方式来类型约束
concept
作为一个新的语法糖,给了我们拆分二者的机会:让上帝归上帝,凯撒归凯撒。使用好
concept
来进行类型约束,enjoy新标准带来的便利吧。希望大家能够有所收获,笔者水平有限。成文之处难免有理解谬误之处,欢迎大家多多讨论,指教。
5.参考资料 【C++雾中风景18:C++20|C++雾中风景18:C++20, 从concept开始】CppReference