C++20学习:concept用法介绍

概述

众所周知,C++的泛型强大,很多语言都学了,比如C#、Java,虽然Java的泛型只是语法糖,不值得一提。

C++的template实际上是创造了一种新的语言,这个语言是编译期的,相当于编译器把代码解释成C++语言,然后再编译成机器指令。STL的诞生,是颠覆性的创举,然而STL所用到的泛型还只是C++template中其中一部分特性。

C++使用template实现泛型算法,我们来看下一个简单的例子。

template<typename T>
T inc(T a)
{
    return ++a;
}

这个简单几行代码,就实现了适合所有C++内建类型的自增函数,甚至只要class支持加法++运算的重载,也都是支持的。

设计一个原则,先宽后严,其实非常困难的。对于C++也不例外。上面的代码,对于T类型的要求,只是概念层面,无法通过函数的声明能够直接看出T的限制。只有用户写完代码,一看才知道有编译错误,而且糟糕的是甚至没有编译错误,真的会怼天怼地。比如:

using namespace std;

template<typename T>
T inc(T a)
{
    return ++a;
}

int main(int argc, const char * argv[])
{
    int a = 0;
    cout << inc(&a) << endl;
    return 0;
}

上面代码inc函数参数a不能传递指针,编译的时候没有错误提示,只有到运行的时候可能会出问题。

另外,template的编译错误提示又做得不好,这也是C++最为诟病的地方。于是concept特性一直呼声比较高。

concept

concept一个用处,就是对T进行限制,让编译器自动检查T是否符合函数的要求。

我们先看下,不用concept,如何判断T是否符合要求:

template<typename T>
T inc(T a)
{
    static_assert(std::is_integral<T>::value);
    return ++a ;
}

int main(int argc, const char * argv[])
{
    int a = 0;
    cout << inc(&a) << endl;
    return 0;
}

这个时候,编译器就会编译错误了,inc函数不能传递指针,T只有满足是整型才可以。虽然可以解决问题,但是T的检查是在函数内实现的,只有出错了,用户才知道。不然从函数inc的声明直观看出T的要求。

我们看看使用concept如何实现的。代码如下:

template<typename T>
concept Integral = std::is_integral<T>::value;

template<Integral T>
T inc(T a)
{
    return ++a ;
}

int main(int argc, const char * argv[])
{
    int a = 0;
    cout << inc(&a) << endl;
    return 0;
}

这个实现,使用concept关键字。如果我们预期,编译不通过,我们在看看编译错误:

main.cpp:40:3: note: constraints not satisfied
main.cpp: In instantiation of ‘T inc(T) [with T = int*]’:
main.cpp:48:19:   required from here
main.cpp:37:9:   required for the satisfaction of ‘Integral<T>[with T = int*]
main.cpp:37:41: note: the expression ‘std::is_integral<_Tp>::value [with _Tp = int*]’ evaluated to ‘false’
   37 | concept Integral = std::is_integral<T>::value;

g++的错误提示,已经很容易看出来,inc函数传递指针是不满足concept的要求的。现状从inc函数的声明,也能直接看出对T的要求,T要是Integer类型。对于函数的设计者来说,你起一个比较直观的名字,就可以传达你的约束了。

上面concept只是一种写法,还有三种写法如下:

  • 写法2
template <typename T>requires Integral<T>
T inc(T a)
{
    return ++a ;
}
  • 写法3
template <typename T>
T inc(T a) requires Integral<T>
{
    return ++a ;
}

这种写法其实和写法2,差别不大,只是requires语句放的位置不同而已。

  • 写法4
Integral auto inc(Integral auto a)
{
    return ++a ;
}

这种也是我们的Bjarne Stroustrup老爷子最喜欢的语法。虽然看上去比较怪异,template关键字没了,多了auto关键字,也许是从c++的实现角度,使用auto关键字比较统一吧。

我觉得第一种写法,比较符合承上启下,与template语法差异不大,没有那么大的语法跳变,比较容易理解。

讨论

一种concept特性,四种写法,买一赠三,不带含糊的,就看你脑袋瓜子够不够用。之所以,为啥要有四种写法。接下来再好好学习,知其然知所以然吧。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值