Skip to content

Commit 9004bbe

Browse files
committed
feat(cpp14): add 01-relaxed-constexpr exercises and solutions
1 parent 008e333 commit 9004bbe

8 files changed

Lines changed: 516 additions & 6 deletions

File tree

book/en/src/cpp14/01-relaxed-constexpr.md

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,51 @@ static_assert(fib(10) == 55, "");
118118
static_assert(fib(0) == 0, "");
119119
```
120120
121-
## II. Notes
121+
## II. Real-World Case — Compile-Time Index Sequences in the STL
122+
123+
> C++14 introduced `std::integer_sequence` and `std::make_index_sequence`, which rely on relaxed constexpr for compile-time integer generation. The examples below cite the vendored [MSVC STL](https://github.com/mcpp-community/d2mcpp/tree/main/msvc-stl) (source: [`msvc-stl/stl/inc/tuple`](https://github.com/mcpp-community/d2mcpp/blob/main/msvc-stl/stl/inc/tuple#L323-L343)); `_Tag` / `_Tpl` are internal tags and types and can be ignored while reading
124+
125+
### std::tuple Compile-Time Construction — index_sequence Unfolds Parameter Packs
126+
127+
`std::tuple` must extract elements one by one from a tuple-like object and construct its own members — this process must happen at compile time. MSVC STL uses `make_index_sequence` to generate compile-time indices and expand parameter packs:
128+
129+
```cpp
130+
// MSVC STL · msvc-stl/stl/inc/tuple (abridged)
131+
template <size_t... _Indices, class _Tpl>
132+
constexpr tuple(_Tag, _Tpl&& _Right, index_sequence<_Indices...>)
133+
: _Mybase(static_cast<_Tpl&&>(_Right)._Get_rest()) {
134+
// _Indices... is a compile-time-generated sequence 0, 1, 2, ... N-1
135+
// Elements are extracted via get<_Indices>(_Right) one by one
136+
}
137+
138+
// Public constructor — uses make_index_sequence to auto-generate indices
139+
explicit(false) constexpr tuple(_Exact_args_t, _Tpl&& _Right)
140+
: tuple(_Tag{}, _STD forward<_Tpl>(_Right),
141+
make_index_sequence<tuple_size_v<remove_reference_t<_Tpl>>>{}) {}
142+
```
143+
144+
`make_index_sequence<N>` generates `index_sequence<0, 1, ..., N-1>` at compile time, enabling the tuple constructor to extract elements via `get<0>` / `get<1>` / ... sequentially. This is the canonical post-C++14 constexpr pattern — iteration is not a runtime concept but achieved through compile-time integer sequences and pack expansion
145+
146+
### std::integer_sequence — the C++14 Compile-Time Integer Carrier
147+
148+
```cpp
149+
// MSVC STL · msvc-stl/stl/inc/utility (abridged)
150+
template <class _Ty, _Ty... _Vals>
151+
struct integer_sequence {
152+
static_assert(is_integral_v<_Ty>,
153+
"integer_sequence<T, I...> requires T to be an integral type.");
154+
};
155+
156+
template <size_t... _Vals>
157+
using index_sequence = integer_sequence<size_t, _Vals...>;
158+
159+
template <size_t _Size>
160+
using make_index_sequence = __make_integer_seq<integer_sequence, size_t, _Size>;
161+
```
162+
163+
> Summary: Both `std::make_index_sequence` and `std::tuple`'s compile-time construction depend on the relaxed constexpr environment introduced in C++14. Without constexpr that supports loops and branches, the standard library would rely entirely on compiler internals for integer sequence generation rather than on the expressive power of the C++ language itself
164+
165+
## III. Notes
122166
123167
### Operations Still Banned in C++14 constexpr
124168
@@ -157,7 +201,7 @@ int main() {
157201

158202
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
159203

160-
## III. Exercise Code
204+
## IV. Exercise Code
161205

162206
### Exercise Code Topics
163207

@@ -170,7 +214,7 @@ A constexpr specifier does not change the ODR linkage in C++14 (C++17 later made
170214
d2x checker relaxed-constexpr
171215
```
172216

173-
## IV. Other
217+
## V. Other
174218

175219
- [Discussion Forum](https://forum.d2learn.org/category/20)
176220
- [d2mcpp Tutorial Repository](https://github.com/mcpp-community/d2mcpp)

book/src/cpp14/01-relaxed-constexpr.md

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,51 @@ static_assert(fib(10) == 55, "");
118118
static_assert(fib(0) == 0, "");
119119
```
120120
121-
## 二、注意事项
121+
## 二、真实案例 - STL 中的 constexpr 编译期索引序列
122+
123+
> C++14 同步引入了 `std::integer_sequence` 和 `std::make_index_sequence`, 它们正是依赖放宽后的 constexpr 才能实现的编译期工具。下面以仓库内置的 [MSVC STL](https://github.com/mcpp-community/d2mcpp/tree/main/msvc-stl) 为例 (源码: [`msvc-stl/stl/inc/tuple`](https://github.com/mcpp-community/d2mcpp/blob/main/msvc-stl/stl/inc/tuple#L323-L343)), `_Tag` / `_Tpl` 是库内部标记和类型, 阅读时可忽略
124+
125+
### std::tuple 的编译期构造 — index_sequence 展开参数包
126+
127+
`std::tuple` 需要从一个"类元组"对象中逐元素提取并构造自己的成员, 这个过程必须在编译期完成。MSVC STL 使用 `make_index_sequence` 生成编译期索引, 配合参数包展开实现:
128+
129+
```cpp
130+
// MSVC STL · msvc-stl/stl/inc/tuple (有删节)
131+
template <size_t... _Indices, class _Tpl>
132+
constexpr tuple(_Tag, _Tpl&& _Right, index_sequence<_Indices...>)
133+
: _Mybase(static_cast<_Tpl&&>(_Right)._Get_rest()) {
134+
// _Indices... 是编译期生成的 0, 1, 2, ... N-1 索引序列
135+
// 通过 get<_Indices>(_Right) 逐元素提取并构造
136+
}
137+
138+
// 公开构造函数 — 用 make_index_sequence 自动生成索引
139+
explicit(false) constexpr tuple(_Exact_args_t, _Tpl&& _Right)
140+
: tuple(_Tag{}, _STD forward<_Tpl>(_Right),
141+
make_index_sequence<tuple_size_v<remove_reference_t<_Tpl>>>{}) {}
142+
```
143+
144+
`make_index_sequence<N>` 在编译期生成 `index_sequence<0, 1, ..., N-1>`, 让 tuple 构造可以用 `get<0>` / `get<1>` / ... 依次提取元素。这正是 C++14 放宽 constexpr 后最经典的应用 — 循环不再是运行期概念, 而是通过编译期整数序列 + 参数包展开来完成
145+
146+
### std::integer_sequence — C++14 引入的编译期整数载体
147+
148+
```cpp
149+
// MSVC STL · msvc-stl/stl/inc/utility (有删节)
150+
template <class _Ty, _Ty... _Vals>
151+
struct integer_sequence {
152+
static_assert(is_integral_v<_Ty>,
153+
"integer_sequence<T, I...> requires T to be an integral type.");
154+
};
155+
156+
template <size_t... _Vals>
157+
using index_sequence = integer_sequence<size_t, _Vals...>;
158+
159+
template <size_t _Size>
160+
using make_index_sequence = __make_integer_seq<integer_sequence, size_t, _Size>;
161+
```
162+
163+
> 小结: `std::make_index_sequence` 和 `std::tuple` 的编译期构造都依赖 C++14 放宽后的 constexpr 环境。没有 loop + branch 的 constexpr, 标准库就只能在编译器内部用黑魔法生成整数序列, 而不能用 C++ 语言自身的表达能力来实现
164+
165+
## 三、注意事项
122166
123167
### C++14 constexpr 仍不支持的操作
124168
@@ -157,7 +201,7 @@ int main() {
157201

158202
把函数标记为 constexpr 并不改变其 ODR 链接属性 (C++17 起 constexpr 函数才隐式 inline), 也不代表所有能 constexpr 的都该 constexpr。如果一个函数几乎只在运行期调用, 加 constexpr 只增加了接口约束, 实际收益很小
159203

160-
## 、练习代码
204+
## 、练习代码
161205

162206
### 练习代码主题
163207

@@ -170,7 +214,7 @@ int main() {
170214
d2x checker relaxed-constexpr
171215
```
172216

173-
## 、其他
217+
## 、其他
174218

175219
- [交流讨论](https://forum.d2learn.org/category/20)
176220
- [d2mcpp教程仓库](https://github.com/mcpp-community/d2mcpp)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// d2mcpp: https://github.com/mcpp-community/d2mcpp
2+
// license: Apache-2.0
3+
// file: dslings/cpp14/01-relaxed-constexpr-0.cpp
4+
//
5+
// Exercise/练习: cpp14 | 01 - relaxed constexpr | constexpr 循环
6+
//
7+
// Tips/提示:
8+
// - C++14 constexpr 函数可以使用 for / while 循环
9+
// - 循环中可以有局部变量和复合语句
10+
//
11+
// Docs/文档:
12+
// - https://en.cppreference.com/w/cpp/language/constexpr
13+
// - https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp14/01-relaxed-constexpr.md
14+
//
15+
// 练习交流讨论: http://forum.d2learn.org/category/20
16+
//
17+
// Auto-Checker/自动检测命令:
18+
//
19+
// d2x checker relaxed-constexpr
20+
//
21+
22+
#include <d2x/cpp/common.hpp>
23+
24+
// constexpr 阶乘 — C++14 可以用循环
25+
constexpr int factorial(int n) {
26+
D2X_YOUR_ANSWER result = 1;
27+
for (int i = 1; D2X_YOUR_ANSWER; ++i) {
28+
result *= i;
29+
}
30+
return result;
31+
}
32+
33+
// constexpr 平方幂 — while 循环版本
34+
constexpr int power(int base, int exp) {
35+
int result = 1;
36+
int i = 0;
37+
while (D2X_YOUR_ANSWER) {
38+
result *= base;
39+
++i;
40+
}
41+
return result;
42+
}
43+
44+
int main() {
45+
46+
// 0. for 循环 — 编译期阶乘
47+
constexpr int f5 = factorial(5);
48+
static_assert(f5 == 120, "factorial(5) should be 120");
49+
d2x_assert_eq(f5, 120);
50+
51+
constexpr int f0 = factorial(0);
52+
static_assert(f0 == 1, "factorial(0) should be 1");
53+
54+
constexpr int f10 = factorial(10);
55+
d2x_assert_eq(f10, 3628800);
56+
57+
// 1. while 循环 — 编译期幂运算
58+
constexpr int p2 = power(2, 3);
59+
static_assert(p2 == 8, "power(2,3) should be 8");
60+
d2x_assert_eq(p2, D2X_YOUR_ANSWER);
61+
62+
constexpr int p5 = power(5, 2);
63+
d2x_assert_eq(p5, 25);
64+
65+
D2X_WAIT
66+
67+
return 0;
68+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// d2mcpp: https://github.com/mcpp-community/d2mcpp
2+
// license: Apache-2.0
3+
// file: dslings/cpp14/01-relaxed-constexpr-1.cpp
4+
//
5+
// Exercise/练习: cpp14 | 01 - relaxed constexpr | constexpr 分支与局部变量
6+
//
7+
// Tips/提示:
8+
// - C++14 constexpr 函数可以使用 if / switch 分支
9+
// - 可以在 constexpr 中声明和使用多个局部变量
10+
//
11+
// Docs/文档:
12+
// - https://en.cppreference.com/w/cpp/language/constexpr
13+
// - https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp14/01-relaxed-constexpr.md
14+
//
15+
// 练习交流讨论: http://forum.d2learn.org/category/20
16+
//
17+
// Auto-Checker/自动检测命令:
18+
//
19+
// d2x checker relaxed-constexpr
20+
//
21+
22+
#include <d2x/cpp/common.hpp>
23+
24+
// constexpr 绝对值 — if 分支
25+
constexpr int my_abs(int x) {
26+
if (D2X_YOUR_ANSWER) {
27+
return -x;
28+
}
29+
return x;
30+
}
31+
32+
// constexpr 月份天数 — switch 分支
33+
constexpr int days_in_month(D2X_YOUR_ANSWER month) {
34+
switch (month) {
35+
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
36+
return 31;
37+
case 4: case 6: case 9: case 11:
38+
return 30;
39+
case 2:
40+
return 28;
41+
D2X_YOUR_ANSWER:
42+
return 0;
43+
}
44+
}
45+
46+
// constexpr 斐波那契 — 多局部变量 + 循环
47+
constexpr int fib(int n) {
48+
int a = 0, b = D2X_YOUR_ANSWER;
49+
for (int i = 0; i < n; ++i) {
50+
int tmp = a + b;
51+
a = b;
52+
b = tmp;
53+
}
54+
return D2X_YOUR_ANSWER;
55+
}
56+
57+
int main() {
58+
59+
// 0. if 分支
60+
constexpr int a1 = my_abs(-10);
61+
static_assert(a1 == 10, "");
62+
d2x_assert_eq(a1, 10);
63+
64+
constexpr int a2 = my_abs(0);
65+
d2x_assert_eq(a2, 0);
66+
67+
// 1. switch 分支
68+
constexpr int d7 = days_in_month(7);
69+
static_assert(d7 == 31, "July has 31 days");
70+
d2x_assert_eq(d7, 31);
71+
72+
constexpr int d2 = days_in_month(2);
73+
d2x_assert_eq(d2, D2X_YOUR_ANSWER);
74+
75+
// 2. 多局部变量 + 循环 — fib
76+
constexpr int f10 = fib(10);
77+
static_assert(f10 == 55, "fib(10) should be 55");
78+
d2x_assert_eq(f10, 55);
79+
80+
constexpr int f0 = fib(0);
81+
d2x_assert_eq(f0, 0);
82+
83+
D2X_WAIT
84+
85+
return 0;
86+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// d2mcpp: https://github.com/mcpp-community/d2mcpp
2+
// license: Apache-2.0
3+
// file: dslings/en/cpp14/01-relaxed-constexpr-0.cpp
4+
//
5+
// Exercise: cpp14 | 01 - relaxed constexpr | constexpr with loops
6+
//
7+
// Tips:
8+
// - C++14 constexpr functions can use for / while loops
9+
// - Loops may contain local variables and compound statements
10+
//
11+
// Docs:
12+
// - https://en.cppreference.com/w/cpp/language/constexpr
13+
// - https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp14/01-relaxed-constexpr.md
14+
//
15+
// Discussion Forum: http://forum.d2learn.org/category/20
16+
//
17+
// Auto-Checker:
18+
//
19+
// d2x checker relaxed-constexpr
20+
//
21+
22+
#include <d2x/cpp/common.hpp>
23+
24+
// constexpr factorial — C++14 allows a for loop
25+
constexpr int factorial(int n) {
26+
D2X_YOUR_ANSWER result = 1;
27+
for (int i = 1; D2X_YOUR_ANSWER; ++i) {
28+
result *= i;
29+
}
30+
return result;
31+
}
32+
33+
// constexpr power — while loop version
34+
constexpr int power(int base, int exp) {
35+
int result = 1;
36+
int i = 0;
37+
while (D2X_YOUR_ANSWER) {
38+
result *= base;
39+
++i;
40+
}
41+
return result;
42+
}
43+
44+
int main() {
45+
46+
// 0. for loop — compile-time factorial
47+
constexpr int f5 = factorial(5);
48+
static_assert(f5 == 120, "factorial(5) should be 120");
49+
d2x_assert_eq(f5, 120);
50+
51+
constexpr int f0 = factorial(0);
52+
static_assert(f0 == 1, "factorial(0) should be 1");
53+
54+
constexpr int f10 = factorial(10);
55+
d2x_assert_eq(f10, 3628800);
56+
57+
// 1. while loop — compile-time power
58+
constexpr int p2 = power(2, 3);
59+
static_assert(p2 == 8, "power(2,3) should be 8");
60+
d2x_assert_eq(p2, D2X_YOUR_ANSWER);
61+
62+
constexpr int p5 = power(5, 2);
63+
d2x_assert_eq(p5, 25);
64+
65+
D2X_WAIT
66+
67+
return 0;
68+
}

0 commit comments

Comments
 (0)