现代C++17-constexpr:一个常态的世界

我们已经连续讲了几讲比较累人的编译期编程了。今天我们还是继续这个话题,但是,相信今天学完之后,你会感觉比之前几讲要轻松很多。C++ 语言里的很多改进,让我们做编译期编程也变得越来越简单了。

初识 constexpr

我们先来看一些例子:

int sqr(int n)

{

return n * n;

}

int main()

{

int a[sqr(3)];

}

想一想,这个代码合法吗?

看过之后,再想想这个代码如何?

int sqr(int n)

{

return n * n;

}

int main()

{

const int n = sqr(3);

int a[sqr(3)];

}

还有这个?

#include <array>

int sqr(int n)

{

return n * n;

}

int main()

{

std::array<int, sqr(3)> a;

}

此外,我们前面模板元编程里的那些类里的 static const int 什么的,你认为它们能用在上面的几种情况下吗?

如果以上问题你都知道正确的答案,那恭喜你,你对 C++ 的理解已经到了一个不错的层次了。但问题依然在那里:这些问题的答案不直观。并且,我们需要一个比模板元编程更方便的进行编译期计算的方法。

在 C++11 引入、在 C++14 得到大幅改进的 constexpr 关键字就是为了解决这些问题而诞生的。它的字面意思是 constant expression,常量表达式。存在两类 constexpr 对象:

constexpr 变量(唉……😓)

constexpr 函数

一个 constexpr 变量是一个编译时完全确定的常数。一个 constexpr 函数至少对于某一组实参可以在编译期间产生一个编译期常数。

注意一个 constexpr 函数不保证在所有情况下都会产生一个编译期常数(因而也是可以作为普通函数来使用的)。编译器也没法通用地检查这点。编译器唯一强制的是:

constexpr 变量必须立即初始化

初始化只能使用字面量或常量表达式,后者不允许调用任何非 constexpr 函数

constexpr 的实际规则当然稍微更复杂些,而且随着 C++ 标准的演进也有着一些变化,特别是对 constexpr 函数如何实现的要求在慢慢放宽。要了解具体情况包括其在不同 C++ 标准中的限制,可以查看参考资料 [1]。下面我们也会回到这个问题略作展开。

拿 constexpr 来改造开头的例子,下面的代码就完全可以工作了:

#include <array>

constexpr int sqr(int n)

{

return n * n;

}

int main()

{

constexpr int n = sqr(3);

std::array<int, n> a;

int b[n];

}

要检验一个 constexpr 函数能不能产生一个真正的编译期常量,可以把结果赋给一个 constexpr 变量。成功的话,我们就确认了,至少在这种调用情况下,我们能真正得到一个编译期常量。

constexpr 和编译期计算

上面这些当然有点用。但如果只有这点用的话,就不值得我专门来写一讲了。更强大的地方在于,使用编译期常量,就跟我们之前的那些类模板里的 static const int 变量一样,是可以进行编译期计算的。

以 [第 13 讲] 提到的阶乘函数为例,和那个版本基本等价的写法是:

constexpr int factorial(int n)

{

if (n == 0) {

return 1;

} else {

return n * factorial(n - 1);

}

}

然后,我们用下面的代码可以验证我们确实得到了一个编译期常量:

int main()

{

constexpr int n = factorial(10);

printf("%d\n", n);

}

编译可以通过,同时,如果我们看产生的汇编代码的话,一样可以直接看到常量 3628800。

这里有一个问题:在这个 constexpr 函数里,是不能写 static_assert(n >= 0) 的。一个 constexpr 函数仍然可以作为普通函数使用——显然,传入一个普通 int 是不能使用静态断言的。替换方法是在 factorial 的实现开头加入:

if (n < 0) {

throw std::invalid_argument(

"Arg must be non-negative");

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员zhi路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值