Skip to content

Commit 008e333

Browse files
committed
docs(cpp14): add 01-relaxed-constexpr book chapter and build wiring
1 parent 1c37ddd commit 008e333

7 files changed

Lines changed: 386 additions & 0 deletions

File tree

book/en/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
# C++14 Core Language Features
3232

3333
- [Generic Lambdas](./cpp14/00-generic-lambdas.md)
34+
- [Relaxed constexpr](./cpp14/01-relaxed-constexpr.md)
3435

3536
# Additional Resources
3637

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
<div align=right>
2+
3+
🌎 [中文] | [English]
4+
</div>
5+
6+
[中文]: ../../cpp14/01-relaxed-constexpr.html
7+
[English]: ./01-relaxed-constexpr.html
8+
9+
# Relaxed constexpr
10+
11+
C++14 significantly relaxed the restrictions on `constexpr` functions — loops, branches, local variables, and multiple statements are now allowed, enabling far more algorithms to execute at compile time
12+
13+
| Book | Video | Code | X |
14+
| --- | --- | --- | --- |
15+
| [cppreference-constexpr](https://en.cppreference.com/w/cpp/language/constexpr) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp14/01-relaxed-constexpr.md) | [Video Explanation]() | [Exercise Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp14/01-relaxed-constexpr-0.cpp) | |
16+
17+
18+
**Why introduced?**
19+
20+
- In C++11, a `constexpr` function body was restricted to a single `return expr;` statement — any loop or branch had to be expressed via recursion and the ternary operator, making the code obscure
21+
- Loops and branches are the most fundamental control flow in practical algorithms; excluding them from constexpr severely limited the scope of compile-time computation
22+
- C++14 allows `for` / `while` / `if` / `switch` and local variables in constexpr functions, making it possible to move runtime algorithms to compile time without rewriting their style
23+
24+
**C++11 vs C++14 constexpr**
25+
26+
```cpp
27+
// C++11: recursion + ternary operator
28+
constexpr int factorial_11(int n) {
29+
return n <= 1 ? 1 : n * factorial_11(n - 1);
30+
}
31+
32+
// C++14: plain loop
33+
constexpr int factorial_14(int n) {
34+
int result = 1;
35+
for (int i = 1; i <= n; ++i) {
36+
result *= i;
37+
}
38+
return result;
39+
}
40+
```
41+
42+
## I. Basic Usage and Scenarios
43+
44+
### Loop Structures — for / while
45+
46+
> constexpr functions can use loops just like normal functions
47+
48+
```cpp
49+
constexpr int sum_to(int n) {
50+
int total = 0;
51+
for (int i = 1; i <= n; ++i) {
52+
total += i;
53+
}
54+
return total;
55+
}
56+
57+
static_assert(sum_to(5) == 15, "");
58+
static_assert(sum_to(100) == 5050, "");
59+
```
60+
61+
### Branch Structures — if / switch
62+
63+
```cpp
64+
constexpr int abs_val(int x) {
65+
if (x < 0) {
66+
return -x;
67+
}
68+
return x;
69+
}
70+
71+
static_assert(abs_val(-42) == 42, "");
72+
static_assert(abs_val(0) == 0, "");
73+
74+
constexpr int day_count(int month) {
75+
switch (month) {
76+
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
77+
return 31;
78+
case 4: case 6: case 9: case 11:
79+
return 30;
80+
case 2:
81+
return 28;
82+
default:
83+
return 0;
84+
}
85+
}
86+
87+
static_assert(day_count(7) == 31, "");
88+
```
89+
90+
### Local Variables and Multiple Statements
91+
92+
```cpp
93+
constexpr double circle_area(double radius) {
94+
const double pi = 3.14159;
95+
double r2 = radius * radius;
96+
return pi * r2;
97+
}
98+
99+
static_assert(circle_area(1.0) == 3.14159, "");
100+
```
101+
102+
### Practical Compile-Time Algorithm — Loop-Based Fibonacci
103+
104+
> Replace recursion with a loop to compute fibonacci at compile time
105+
106+
```cpp
107+
constexpr int fib(int n) {
108+
int a = 0, b = 1;
109+
for (int i = 0; i < n; ++i) {
110+
int tmp = a + b;
111+
a = b;
112+
b = tmp;
113+
}
114+
return a;
115+
}
116+
117+
static_assert(fib(10) == 55, "");
118+
static_assert(fib(0) == 0, "");
119+
```
120+
121+
## II. Notes
122+
123+
### Operations Still Banned in C++14 constexpr
124+
125+
The following operations remain forbidden in C++14 constexpr functions:
126+
127+
- `goto` statements
128+
- `try` / `catch` exception handling
129+
- `static` or `thread_local` local variables
130+
- inline assembly
131+
- uninitialized local variables
132+
133+
### The "Dual Nature" of constexpr Functions
134+
135+
A constexpr function can execute at compile time or at runtime — depending on the call context:
136+
137+
```cpp
138+
constexpr int fib(int n) {
139+
int a = 0, b = 1;
140+
for (int i = 0; i < n; ++i) {
141+
int tmp = a + b;
142+
a = b;
143+
b = tmp;
144+
}
145+
return a;
146+
}
147+
148+
static_assert(fib(10) == 55, ""); // compile-time
149+
150+
int main() {
151+
int n = std::rand() % 20;
152+
return fib(n); // runtime — same code
153+
}
154+
```
155+
156+
### constexpr Is Not a Drop-In Replacement for inline
157+
158+
A constexpr specifier does not change the ODR linkage in C++14 (C++17 later made constexpr functions implicitly inline), nor does it mean every function that can be constexpr should be. If a function is almost always called at runtime, adding constexpr increases the interface constraint with little practical benefit
159+
160+
## III. Exercise Code
161+
162+
### Exercise Code Topics
163+
164+
- 0 - [constexpr Loops — Compile-Time Factorial and Power](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp14/01-relaxed-constexpr-0.cpp)
165+
- 1 - [constexpr Branches and Local Variables — Compile-Time Conditionals and Fibonacci](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp14/01-relaxed-constexpr-1.cpp)
166+
167+
### Exercise Auto-Checker Command
168+
169+
```
170+
d2x checker relaxed-constexpr
171+
```
172+
173+
## IV. Other
174+
175+
- [Discussion Forum](https://forum.d2learn.org/category/20)
176+
- [d2mcpp Tutorial Repository](https://github.com/mcpp-community/d2mcpp)
177+
- [Tutorial Video List](https://space.bilibili.com/65858958/lists/5208246)
178+
- [Tutorial Support Tool - xlings](https://github.com/openxlings/xlings)

book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
# C++14核心语言特性
3232

3333
- [泛型 lambda - generic lambdas](./cpp14/00-generic-lambdas.md)
34+
- [放宽的 constexpr - relaxed constexpr](./cpp14/01-relaxed-constexpr.md)
3435

3536
# 其他
3637

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
<div align=right>
2+
3+
🌎 [中文] | [English]
4+
</div>
5+
6+
[中文]: ./01-relaxed-constexpr.html
7+
[English]: ../en/cpp14/01-relaxed-constexpr.html
8+
9+
# 放宽的 constexpr - relaxed constexpr
10+
11+
C++14 大幅放宽了 `constexpr` 函数的限制——允许在编译期函数中使用循环、分支、局部变量和多条语句, 让更多的算法可以在编译期执行
12+
13+
| Book | Video | Code | X |
14+
| --- | --- | --- | --- |
15+
| [cppreference-constexpr](https://en.cppreference.com/w/cpp/language/constexpr) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp14/01-relaxed-constexpr.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp14/01-relaxed-constexpr-0.cpp) | |
16+
17+
18+
**为什么引入?**
19+
20+
- C++11 的 `constexpr` 函数体必须是 `return expr;` 一条语句, 任何循环或分支都要用递归 + 三元运算符表达, 代码晦涩
21+
- 实际算法中循环和分支是最基本的控制流, 将它们排除在 constexpr 之外严重限制了编译期计算的应用范围
22+
- C++14 允许 constexpr 函数使用 `for` / `while` / `if` / `switch` 和局部变量, 让"把运行期算法搬到编译期"成为可能, 而不需要改写风格
23+
24+
**C++11 vs C++14 constexpr**
25+
26+
```cpp
27+
// C++11: 只能用递归 + 三元运算符
28+
constexpr int factorial_11(int n) {
29+
return n <= 1 ? 1 : n * factorial_11(n - 1);
30+
}
31+
32+
// C++14: 可以写正常的循环
33+
constexpr int factorial_14(int n) {
34+
int result = 1;
35+
for (int i = 1; i <= n; ++i) {
36+
result *= i;
37+
}
38+
return result;
39+
}
40+
```
41+
42+
## 一、基础用法和场景
43+
44+
### 循环结构 — for / while
45+
46+
> constexpr 函数中可以像普通函数一样写循环
47+
48+
```cpp
49+
constexpr int sum_to(int n) {
50+
int total = 0;
51+
for (int i = 1; i <= n; ++i) {
52+
total += i;
53+
}
54+
return total;
55+
}
56+
57+
static_assert(sum_to(5) == 15, "");
58+
static_assert(sum_to(100) == 5050, "");
59+
```
60+
61+
### 分支结构 — if / switch
62+
63+
```cpp
64+
constexpr int abs_val(int x) {
65+
if (x < 0) {
66+
return -x;
67+
}
68+
return x;
69+
}
70+
71+
static_assert(abs_val(-42) == 42, "");
72+
static_assert(abs_val(0) == 0, "");
73+
74+
constexpr int day_count(int month) {
75+
switch (month) {
76+
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
77+
return 31;
78+
case 4: case 6: case 9: case 11:
79+
return 30;
80+
case 2:
81+
return 28;
82+
default:
83+
return 0;
84+
}
85+
}
86+
87+
static_assert(day_count(7) == 31, "");
88+
```
89+
90+
### 局部变量与多条语句
91+
92+
```cpp
93+
constexpr double circle_area(double radius) {
94+
const double pi = 3.14159;
95+
double r2 = radius * radius;
96+
return pi * r2;
97+
}
98+
99+
static_assert(circle_area(1.0) == 3.14159, "");
100+
```
101+
102+
### 编译期实用算法 — 循环斐波那契
103+
104+
> 用循环替代递归, 在编译期计算斐波那契数列
105+
106+
```cpp
107+
constexpr int fib(int n) {
108+
int a = 0, b = 1;
109+
for (int i = 0; i < n; ++i) {
110+
int tmp = a + b;
111+
a = b;
112+
b = tmp;
113+
}
114+
return a;
115+
}
116+
117+
static_assert(fib(10) == 55, "");
118+
static_assert(fib(0) == 0, "");
119+
```
120+
121+
## 二、注意事项
122+
123+
### C++14 constexpr 仍不支持的操作
124+
125+
以下操作在 C++14 constexpr 函数中仍被禁止:
126+
127+
- `goto` 语句
128+
- `try` / `catch` 异常处理
129+
- `static` 或 `thread_local` 局部变量
130+
- 内联汇编
131+
- 未初始化的局部变量
132+
133+
### constexpr 函数的"两面性"
134+
135+
constexpr 函数可以在编译期执行, 也可以在运行期执行——取决于调用上下文:
136+
137+
```cpp
138+
constexpr int fib(int n) {
139+
int a = 0, b = 1;
140+
for (int i = 0; i < n; ++i) {
141+
int tmp = a + b;
142+
a = b;
143+
b = tmp;
144+
}
145+
return a;
146+
}
147+
148+
static_assert(fib(10) == 55, ""); // 编译期执行
149+
150+
int main() {
151+
int n = std::rand() % 20;
152+
return fib(n); // 运行期执行, 同一份代码
153+
}
154+
```
155+
156+
### constexpr 不是 inline 的替代
157+
158+
把函数标记为 constexpr 并不改变其 ODR 链接属性 (C++17 起 constexpr 函数才隐式 inline), 也不代表所有能 constexpr 的都该 constexpr。如果一个函数几乎只在运行期调用, 加 constexpr 只增加了接口约束, 实际收益很小
159+
160+
## 三、练习代码
161+
162+
### 练习代码主题
163+
164+
- 0 - [constexpr 循环 — 编译期阶乘和幂运算](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp14/01-relaxed-constexpr-0.cpp)
165+
- 1 - [constexpr 分支和局部变量 — 编译期条件判断与斐波那契](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp14/01-relaxed-constexpr-1.cpp)
166+
167+
### 练习代码自动检测命令
168+
169+
```
170+
d2x checker relaxed-constexpr
171+
```
172+
173+
## 四、其他
174+
175+
- [交流讨论](https://forum.d2learn.org/category/20)
176+
- [d2mcpp教程仓库](https://github.com/mcpp-community/d2mcpp)
177+
- [教程视频列表](https://space.bilibili.com/65858958/lists/5208246)
178+
- [教程支持工具-xlings](https://github.com/openxlings/xlings)

dslings/cpp14/xmake.lua

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,11 @@ target("cpp14-00-generic-lambdas-0")
99
target("cpp14-00-generic-lambdas-1")
1010
set_kind("binary")
1111
add_files("00-generic-lambdas-1.cpp")
12+
13+
target("cpp14-01-relaxed-constexpr-0")
14+
set_kind("binary")
15+
add_files("01-relaxed-constexpr-0.cpp")
16+
17+
target("cpp14-01-relaxed-constexpr-1")
18+
set_kind("binary")
19+
add_files("01-relaxed-constexpr-1.cpp")

dslings/en/cpp14/xmake.lua

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,13 @@ target("cpp14-00-generic-lambdas-0")
99
target("cpp14-00-generic-lambdas-1")
1010
set_kind("binary")
1111
add_files("00-generic-lambdas-1.cpp")
12+
13+
-- target: cpp14-01-relaxed-constexpr
14+
15+
target("cpp14-01-relaxed-constexpr-0")
16+
set_kind("binary")
17+
add_files("01-relaxed-constexpr-0.cpp")
18+
19+
target("cpp14-01-relaxed-constexpr-1")
20+
set_kind("binary")
21+
add_files("01-relaxed-constexpr-1.cpp")

0 commit comments

Comments
 (0)