From fb91f1584acf3727f2185cc612207e7b8a0d9c7c Mon Sep 17 00:00:00 2001 From: Changkun Ou Date: Wed, 10 Jul 2019 10:18:23 +0200 Subject: [PATCH] update: move std::variant to container chapter --- book/zh-cn/04-containers.md | 29 +++++++++++++++++------------ book/zh-cn/10-cpp20.md | 27 +-------------------------- code/4/4.3.cpp | 29 +++++++++++++++++------------ 3 files changed, 35 insertions(+), 50 deletions(-) diff --git a/book/zh-cn/04-containers.md b/book/zh-cn/04-containers.md index e7b5c89..56a3301 100644 --- a/book/zh-cn/04-containers.md +++ b/book/zh-cn/04-containers.md @@ -228,22 +228,27 @@ int index = 1; std::get(t); ``` -那么要怎么处理?答案是,**标准库做不到**。这里介绍一个使用 `boost::variant` 配合变长模板参数的黑魔法: +那么要怎么处理?答案是,使用 `std::variant<>`(C++ 17 引入),提供给 `variant<>` 的类型模板参数 +可以让一个 `variant<>` 从而容纳提供的几种类型的变量(在其他语言,例如 Python/JavaScript 等,表现为动态类型): ```cpp -#include +#include template -boost::variant _tuple_index(size_t i, const std::tuple& tpl) { -if (i == n) - return std::get(tpl); -else if (n == sizeof...(T) - 1) - throw std::out_of_range("越界."); -else - return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl); +constexpr std::variant _tuple_index(const std::tuple& tpl, size_t i) { + if constexpr (n >= sizeof...(T)) + throw std::out_of_range("越界."); + if (i == n) + return std::variant{ std::in_place_index, std::get(tpl) }; + return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(tpl, i); } template -boost::variant tuple_index(size_t i, const std::tuple& tpl) { - return _tuple_index<0>(i, tpl); +constexpr std::variant tuple_index(const std::tuple& tpl, size_t i) { + return _tuple_index<0>(tpl, i); +} +template +std::ostream & operator<< (std::ostream & s, std::variant const & v) { + std::visit([&](auto && x){ s << x;}, v); + return s; } ``` @@ -251,7 +256,7 @@ boost::variant tuple_index(size_t i, const std::tuple& tpl) { ```cpp int i = 1; -std::cout << tuple_index(i, t) << std::endl; +std::cout << tuple_index(t, i) << std::endl; ``` ### 元组合并与遍历 diff --git a/book/zh-cn/10-cpp20.md b/book/zh-cn/10-cpp20.md index c2bd8a9..611c67c 100644 --- a/book/zh-cn/10-cpp20.md +++ b/book/zh-cn/10-cpp20.md @@ -6,7 +6,7 @@ order: 10 # 第 10 章 展望:C++20 简介 -> 内容修订中, 目前内容为第一版中对 C++17 的展望 +> 内容修订中 [TOC] @@ -50,31 +50,6 @@ template void foo() { foo<10>(); // value 被推导为 int 类型 ``` -### std::variant<> - -熟悉 `boost` 的人应该很早就听说过 `variant<>` 了。`variant<>` 可以用于存储和操作不同类型的对象。我们在前面([对标准库的扩充:新增容器](./4.containers.md))对于迭代 `std::tuple` 时,简单使用了 `boost::variant<>`。提供给 `variant<>` 的类型模板参数可以让一个 `variant<>` 从而容纳提供的几种类型的变量(在其他语言(例如 Python/JavaScript 等)表现为动态类型)。 - -C++17 正式将 `variant<>` 纳入标准库,摇身一变成为 `std::variant<>`,有了它之后,我们可以将前面的代码更改为: - -```cpp -#include -template -std::variant _tuple_index(size_t i, const std::tuple& tpl) { - if (i == n) - return std::get(tpl); - else if (n == sizeof...(Args) - 1) - throw std::out_of_range("越界."); - else - return _tuple_index<(n < sizeof...(Args)-1 ? n+1 : 0)>(i, tpl); - } -template -std::variant tuple_index(size_t i, const std::tuple& tpl) { - return _tuple_index<0>(i, tpl); -} -``` - - - ## 10.2 未入选特性 C++ 组委会在讨论投票最终确定 C++17 有很多提案,诸如 **Concepts**/**Ranges**/**Module** 等等,其中最受关注的就是 **Concepts**,可惜这一提案最终被拒,作为技术规范(Technical Specifications, TS) 将其发布。 diff --git a/code/4/4.3.cpp b/code/4/4.3.cpp index 93e70fa..5a88271 100644 --- a/code/4/4.3.cpp +++ b/code/4/4.3.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include auto get_student(int id) { @@ -24,17 +24,16 @@ auto get_student(int id) } template -boost::variant _tuple_index(size_t i, const std::tuple& tpl) { - if (i == n) - return std::get(tpl); - else if (n == sizeof...(T) - 1) +constexpr std::variant _tuple_index(const std::tuple& tpl, size_t i) { + if constexpr (n >= sizeof...(T)) throw std::out_of_range("越界."); - else - return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl); + if (i == n) + return std::variant{ std::in_place_index, std::get(tpl) }; + return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(tpl, i); } template -boost::variant tuple_index(size_t i, const std::tuple& tpl) { - return _tuple_index<0>(i, tpl); +constexpr std::variant tuple_index(const std::tuple& tpl, size_t i) { + return _tuple_index<0>(tpl, i); } template @@ -42,6 +41,12 @@ auto tuple_len(T &tpl) { return std::tuple_size::value; } +template +std::ostream & operator<< (std::ostream & s, std::variant const & v) { + std::visit([&](auto && x){ s << x;}, v); + return s; +} + int main() { auto student = get_student(0); @@ -71,7 +76,7 @@ int main() auto new_tuple = std::tuple_cat(get_student(1), std::move(t)); // 迭代 - for(int i = 0; i != tuple_len(new_tuple); ++i) - // 运行期索引 - std::cout << tuple_index(i, new_tuple) << std::endl; + for(int i = 0; i != tuple_len(new_tuple); ++i) { + std::cout << tuple_index(new_tuple, i) << std::endl; // 运行期索引 + } }