constexpr 是 c++11 引入的关键字,用于编译时常量和常量表达式。而 c++17 将这一特性做了增强,引入了 constexpr if
,
使得编译器在编译时(compile time)能够做分支判断,从而有条件的编译代码。
下面可以通过一个简单的例子来看看constexpr if
的用法:
1#include <iostream>
2#include <type_traits>
3
4template<typename T> auto getValue(T t)
5{
6 if constexpr (std::is_pointer<T>::value) {
7 return *t;
8 } else {
9 return t;
10 }
11}
12
13int main(int argc, char* argv[])
14{
15 int a = 10;
16 int* b = &a;
17 getValue(a);
18 getValue(b);
19 return 0;
20}
其实和普通的条件判断区别不大,只不过constexpr if
中的条件是常量表达式,可以在编译时确定条件表达式的结果,从而选择编译对应的分支代码。
我们可以将上述代码编译成汇编来进一步分析:
1auto getValue<int>(int):
2 push rbp
3 mov rbp, rsp
4 mov DWORD PTR [rbp-4], edi
5 mov eax, DWORD PTR [rbp-4]
6 pop rbp
7 ret
8auto getValue<int*>(int*):
9 push rbp
10 mov rbp, rsp
11 mov QWORD PTR [rbp-8], rdi
12 mov rax, QWORD PTR [rbp-8]
13 mov eax, DWORD PTR [rax]
14 pop rbp
15 ret
16......
这里可以看到,生成的getValue<int>
和getValue<int*>
两个版本的函数分别保留了对应类型的分支逻辑,而没有了条件判断。
至此我们对constexpr if
的用法有了初步的认知,下面来通过元编程来加深对其的理解。
constexpr if 在元编程中的应用
说到元编程,我们就从元编程的"hello world"程序——计算阶乘开始,我们先写出一个不使用constexpr if
的阶乘:
1#include <iostream>
2
3template<int N> struct Factorial
4{
5 static constexpr int value = N * Factorial<N - 1>::value;
6};
7
8template<> struct Factorial<1>
9{
10 static constexpr int value = 1;
11};
12
13template<int N> inline constexpr int Factorial_v = Factorial<N>::value;
14
15int main(int argc, char* argv[])
16{
17 std::cout << Factorial_v<1> << std::endl;
18 std::cout << Factorial_v<2> << std::endl;
19 std::cout << Factorial_v<3> << std::endl;
20 std::cout << Factorial_v<4> << std::endl;
21 return 0;
22}
这段代码非常简单,没什么可解释的,这里之所以拿出来是为了将其使用constexpr if
进行重写:
1#include <iostream>
2
3template<int N> constexpr int factorial()
4{
5 if constexpr (N >= 2) {
6 return N * factorial<N - 1>();
7 } else {
8 return N;
9 }
10}
11
12int main(int argc, char* argv[])
13{
14 std::cout << factorial<1>() << std::endl;
15 std::cout << factorial<2>() << std::endl;
16 std::cout << factorial<3>() << std::endl;
17 std::cout << factorial<4>() << std::endl;
18 return 0;
19}
通过上面的改写,可以很容易发现,在不使用constexpr if
的时候,我们需要额外的对Factorial
模板类做特例化,来定义递归的结束位置。而有了constexpr if
我们可以像写正常的函数那样写出能具有常量特性的函数,在编译期计算阶乘。
同样地,我们也可以很容易将fibonacci函数改造成constexpr if
版本,这里就不再赘述了。
评论