Skip to content

Commit fb91f15

Browse files
committed
update: move std::variant to container chapter
1 parent 942dd4b commit fb91f15

File tree

3 files changed

+35
-50
lines changed

3 files changed

+35
-50
lines changed

book/zh-cn/04-containers.md

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -228,30 +228,35 @@ int index = 1;
228228
std::get<index>(t);
229229
```
230230

231-
那么要怎么处理?答案是,**标准库做不到**。这里介绍一个使用 `boost::variant` 配合变长模板参数的黑魔法:
231+
那么要怎么处理?答案是,使用 `std::variant<>`(C++ 17 引入),提供给 `variant<>` 的类型模板参数
232+
可以让一个 `variant<>` 从而容纳提供的几种类型的变量(在其他语言,例如 Python/JavaScript 等,表现为动态类型):
232233

233234
```cpp
234-
#include <boost/variant.hpp>
235+
#include <variant>
235236
template <size_t n, typename... T>
236-
boost::variant<T...> _tuple_index(size_t i, const std::tuple<T...>& tpl) {
237-
if (i == n)
238-
return std::get<n>(tpl);
239-
else if (n == sizeof...(T) - 1)
240-
throw std::out_of_range("越界.");
241-
else
242-
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
237+
constexpr std::variant<T...> _tuple_index(const std::tuple<T...>& tpl, size_t i) {
238+
if constexpr (n >= sizeof...(T))
239+
throw std::out_of_range("越界.");
240+
if (i == n)
241+
return std::variant<T...>{ std::in_place_index<n>, std::get<n>(tpl) };
242+
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(tpl, i);
243243
}
244244
template <typename... T>
245-
boost::variant<T...> tuple_index(size_t i, const std::tuple<T...>& tpl) {
246-
return _tuple_index<0>(i, tpl);
245+
constexpr std::variant<T...> tuple_index(const std::tuple<T...>& tpl, size_t i) {
246+
return _tuple_index<0>(tpl, i);
247+
}
248+
template <typename T0, typename ... Ts>
249+
std::ostream & operator<< (std::ostream & s, std::variant<T0, Ts...> const & v) {
250+
std::visit([&](auto && x){ s << x;}, v);
251+
return s;
247252
}
248253
```
249254
250255
这样我们就能:
251256
252257
```cpp
253258
int i = 1;
254-
std::cout << tuple_index(i, t) << std::endl;
259+
std::cout << tuple_index(t, i) << std::endl;
255260
```
256261

257262
### 元组合并与遍历

book/zh-cn/10-cpp20.md

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ order: 10
66

77
# 第 10 章 展望:C++20 简介
88

9-
> 内容修订中, 目前内容为第一版中对 C++17 的展望
9+
> 内容修订中
1010
1111
[TOC]
1212

@@ -50,31 +50,6 @@ template <auto value> void foo() {
5050
foo<10>(); // value 被推导为 int 类型
5151
```
5252

53-
### std::variant<>
54-
55-
熟悉 `boost` 的人应该很早就听说过 `variant<>` 了。`variant<>` 可以用于存储和操作不同类型的对象。我们在前面([对标准库的扩充:新增容器](./4.containers.md))对于迭代 `std::tuple` 时,简单使用了 `boost::variant<>`。提供给 `variant<>` 的类型模板参数可以让一个 `variant<>` 从而容纳提供的几种类型的变量(在其他语言(例如 Python/JavaScript 等)表现为动态类型)。
56-
57-
C++17 正式将 `variant<>` 纳入标准库,摇身一变成为 `std::variant<>`,有了它之后,我们可以将前面的代码更改为:
58-
59-
```cpp
60-
#include <variant>
61-
template <size_t n, typename... Args>
62-
std::variant<Args...> _tuple_index(size_t i, const std::tuple<Args...>& tpl) {
63-
if (i == n)
64-
return std::get<n>(tpl);
65-
else if (n == sizeof...(Args) - 1)
66-
throw std::out_of_range("越界.");
67-
else
68-
return _tuple_index<(n < sizeof...(Args)-1 ? n+1 : 0)>(i, tpl);
69-
}
70-
template <typename... Args>
71-
std::variant<Args...> tuple_index(size_t i, const std::tuple<Args...>& tpl) {
72-
return _tuple_index<0>(i, tpl);
73-
}
74-
```
75-
76-
77-
7853
## 10.2 未入选特性
7954

8055
C++ 组委会在讨论投票最终确定 C++17 有很多提案,诸如 **Concepts**/**Ranges**/**Module** 等等,其中最受关注的就是 **Concepts**,可惜这一提案最终被拒,作为技术规范(Technical Specifications, TS) 将其发布。

code/4/4.3.cpp

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
#include <tuple>
1111
#include <iostream>
12-
#include <boost/variant.hpp>
12+
#include <variant>
1313

1414
auto get_student(int id)
1515
{
@@ -24,24 +24,29 @@ auto get_student(int id)
2424
}
2525

2626
template <size_t n, typename... T>
27-
boost::variant<T...> _tuple_index(size_t i, const std::tuple<T...>& tpl) {
28-
if (i == n)
29-
return std::get<n>(tpl);
30-
else if (n == sizeof...(T) - 1)
27+
constexpr std::variant<T...> _tuple_index(const std::tuple<T...>& tpl, size_t i) {
28+
if constexpr (n >= sizeof...(T))
3129
throw std::out_of_range("越界.");
32-
else
33-
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
30+
if (i == n)
31+
return std::variant<T...>{ std::in_place_index<n>, std::get<n>(tpl) };
32+
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(tpl, i);
3433
}
3534
template <typename... T>
36-
boost::variant<T...> tuple_index(size_t i, const std::tuple<T...>& tpl) {
37-
return _tuple_index<0>(i, tpl);
35+
constexpr std::variant<T...> tuple_index(const std::tuple<T...>& tpl, size_t i) {
36+
return _tuple_index<0>(tpl, i);
3837
}
3938

4039
template <typename T>
4140
auto tuple_len(T &tpl) {
4241
return std::tuple_size<T>::value;
4342
}
4443

44+
template <typename T0, typename ... Ts>
45+
std::ostream & operator<< (std::ostream & s, std::variant<T0, Ts...> const & v) {
46+
std::visit([&](auto && x){ s << x;}, v);
47+
return s;
48+
}
49+
4550
int main()
4651
{
4752
auto student = get_student(0);
@@ -71,7 +76,7 @@ int main()
7176
auto new_tuple = std::tuple_cat(get_student(1), std::move(t));
7277

7378
// 迭代
74-
for(int i = 0; i != tuple_len(new_tuple); ++i)
75-
// 运行期索引
76-
std::cout << tuple_index(i, new_tuple) << std::endl;
79+
for(int i = 0; i != tuple_len(new_tuple); ++i) {
80+
std::cout << tuple_index(new_tuple, i) << std::endl; // 运行期索引
81+
}
7782
}

0 commit comments

Comments
 (0)