-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path02-1-constexpr.cpp
More file actions
167 lines (129 loc) · 3.74 KB
/
Copy path02-1-constexpr.cpp
File metadata and controls
167 lines (129 loc) · 3.74 KB
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#include <iostream>
using namespace std;
/*
1. For modern C++, prefer constexpr or const instead of #define for constants.
2. const does NOT necessarily mean compile-time constant.
3. constexpr doesn't mean "this function can ONLY execute at compile time."
- This function is capable of being evaluated at compile time when given compile-time inputs.
constexpr int square(int x) {
return x * x;
}
int n;
cin >> n;
int result = square(n); // runtime
Modern C++ Recommendations:
- Use constexpr when the value is known at compile time and you want the compiler to guarantee it.
- Use const when the value should not change after initialization but may only be known at runtime.
- Avoid #define for constants. It is still useful for preprocessor tasks such as conditional compilation
Do I need text substitution before compilation?
│
Yes ─────────► #define
│
No
│
Is the value guaranteed to be known at compile time?
│
Yes ─────────► constexpr
│
No
│
Should the value stay unchanged after initialization?
│
Yes ─────────► const
│
No
│
Use a regular variable
*/
#define LEN 10
int len_foo() {
int i = 2;
return i;
}
constexpr int len_foo_constexpr() {
return 5;
}
constexpr int fibonacci(const int n) {
return n == 1 || n == 2 ? 1 : fibonacci(n - 1) + fibonacci (n - 2);
}
constexpr int fibonacci_(const int n) {
if(n == 1) return 1;
if(n == 2) return 1;
return fibonacci_(n-1) + fibonacci_(n-2);
}
int getValue(int a) {
return a + a;
}
// x cannot change, but its value is determined at runtime.
const int x = getValue(3);
constexpr int square(int x) {
return x*x;
}
/*
So when should you use each?
1. Use #define
Mostly for things that genuinely belong to the preprocessor, such as conditional compilation:
*/
#ifdef DEBUG
// debug code
#endif
/*
For ordinary constants, don't do:
#define MAX_SIZE 100
Prefer:
constexpr int MAX_SIZE = 100;
*/
/*
2. Use const when you want read-only data
You do not want the name to be modified but you abviosly cannot calculate it at compile time:
*/
string getUsername();
const string name = getUsername();
/*
3. Use constexpr when the value/function should be compile-time capable
*/
constexpr int MAX_SIZE = 100;
constexpr double PI = 3.141592653589793;
constexpr int factorial(int n) {
return n == 0 || n == 1 ? 1 : factorial(n - 1) * n;
}
/*
Use it when the value is fundamentally a compile-time constant.
#define
↓
preprocessor feature
const
↓
C++ type system
↓
read-only
constexpr
↓
C++ type system
↓
read-only + compile-time evaluable
*/
int main() {
system("clear");
char arr_1[10]; // legal
char arr_2[LEN]; // legal
int len = 10;
char arr_3[len]; // illegal
const int len_2 = len + 1;
constexpr int len_2_conxtexpr = 1 + 2 + 3;
char arr_4[len_2]; // illegal
char arr_5[len_2_conxtexpr]; // legal
char arr_6[len_foo() + 5]; // illegal
char arr_7[len_foo_constexpr() + 1]; // legal
cout << fibonacci(10) << endl;
cout << fibonacci_(10) << endl;
int t;
cin >> t;
const int T = t;
// constexpr int T = t; // illegal
cout << T << endl;
int n;
cin >> n;
int result = square(5); // This function is capable of being evaluated at compile time when given compile-time inputs.
return 0;
}