编译期计算

# 原因

很多时候我们不希望代码的某些计算部分在运行阶段出现从而占用cpu,想让它们在编译时期就计算好,尤其是一些很繁琐无聊又要重复计算很多次的部分

# 方法

我们都知道 c++ 的模板是在编译期会被编译器展开(展开后的代码在 c++Insight (opens new window) 就可以看),即如果有一个函数模板被使用

template<typename T>
void print (T x) { std::cout << x << std::endl; }

int main () {
    print(10);
}
1
2
3
4
5
6

那么它被展开后为

template<typename T>
void print(T val)
{
  std::cout << val;
}


/* First instantiated from: insights.cpp:34 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void print<int>(int val)
{
  std::cout.operator<<(val);
}
#endif


int main()
{
  print(10);
  return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

template 是在编译期展开的,全局常量也是如此,那么两者结合在一起使用了话类似于递归地进行轮流展开,有一个出口,那么它们就可以完成编译器的计算了
下面用具体的例子来说明

# 单变量函数

一个变量,计算 2n2^n 的值
既然我们需要递归地展开函数模板,所以正常的时候就是一个递归模板

template<int n>
class Pow2 {
public:
    static const int res = Pow2<n - 1>::res;
};
1
2
3
4
5

2n2^n 的结果需要展开 2n12^{n-1} 的结果进行计算
然后递归还需要出口,就添加一个

template<>
class Pow2<0> {
public:
    static const int res = 1;
};
1
2
3
4
5

std::cout << Pow2<3>::res; 输出一下得到 8 ,成功,在 insights 里面再看一下

template<int n>
class Pow2
{
  
  public: 
  static const int res = (Pow2<n - 1>::res * 2) % 10;
};

/* First instantiated from: insights.cpp:20 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class Pow2<2>
{
  
  public: 
  static const int res = (Pow2<1>::res * 2) % 10;
};

#endif
/* First instantiated from: insights.cpp:29 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class Pow2<3>
{
  
  public: 
  static const int res = (Pow2<2>::res * 2) % 10;
};

#endif

template<>
class Pow2<1>
{
  
  public: 
  static const int res = 2;
};



int main()
{
  std::cout.operator<<(Pow2<3>::res);
  return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

好了都展开出来了

# 多变量函数

上面的既然有了,接下来搞一个两个变量的复杂一点的
编译期计算 gcd(a,b)

也是正常的递归部分

template<int a, int b>
class Gcd {
public:
    static const int res = Gcd<b, a % b>::res;
};
1
2
3
4
5

众所周知 gcd 辗转相除的出口在 b = 0 时返回 a ,那么说明 a 时可变的,所以让 a 自己作为模板参数引入

template<int a>
class Gcd<a, 0> {
public:
    static const int res = a;
};
1
2
3
4
5

好了测试一下 td::cout << Gcd<12, 9>::res << std::endl; 输出 33 正确,看一下模板展开后

#include <iostream>
#include <time.h>
#include <random>
#include <bits/stdc++.h>

template<int a, int b>
class Gcd
{
  
  public: 
  static const int res = Gcd<b, a % b>::res;
};

/* First instantiated from: insights.cpp:9 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class Gcd<9, 3>
{
  
  public: 
  static const int res = Gcd<3, 0>::res;
};

#endif
/* First instantiated from: insights.cpp:9 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class Gcd<3, 0>
{
  
  public: 
  static const int res = 3;
};

#endif
/* First instantiated from: insights.cpp:18 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class Gcd<12, 9>
{
  
  public: 
  static const int res = Gcd<9, 3>::res;
};

#endif

template<int a>
class Gcd<a, 0>
{
  
  public: 
  static const int res = a;
};



int main()
{
  std::cout.operator<<(Gcd<12, 9>::res);
  return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

嗯辗转相除的所有参数都已经暴露出来了,结束

Last Updated: 3/13/2023, 4:54:05 PM