漂亮印刷元组的解剖学

Anatomy of pretty print tuple

本文关键字:解剖学 元组 漂亮      更新时间:2023-10-16

不久前,这里发布了打印std::tuple的解决方案。在大多数情况下,我明白发生了什么。不过,我无法理解print_tuple函数中发生了什么。

template<class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple(std::basic_ostream<Ch,Tr>& os, Tuple const& t, seq<Is...>){
  using swallow = int[];
  (void)swallow{0, (void(os << (Is == 0? "" : ", ") << std::get<Is>(t)), 0)...};
}

我不明白这个函数的主体发生了什么。据我所知,这与开箱有关 Is .我明白了,Is == 0正在检查我们是否处于头部元素。

这到底是怎么回事呢?

让我们通过一个带有任意元组的示例,例如:

using Tuple = tuple<char, int, string>;

因此,调用我们的函数的整数序列是:

seq<0, 1, 2>

而我们在体内的背包扩张是:

(void)swallow{0, (void(os << (Is == 0? "" : ", ") << std::get<Is>(t)), 0)...};

如果我们按照编译器的方式手动扩展它,则变为:

(void)swallow{0,
              (void(os << (0 == 0? "" : ", ") << std::get<0>(t)), 0),
              (void(os << (1 == 0? "" : ", ") << std::get<1>(t)), 0),
              (void(os << (2 == 0? "" : ", ") << std::get<2>(t)), 0)
              };

然后评估分支:

(void)swallow{0,
              (void(os << "" << std::get<0>(t)), 0),
              (void(os << ", " << std::get<1>(t)), 0),
              (void(os << ", " << std::get<2>(t)), 0)
              };

也就是说,我们正在构造一个 4 0 s 的整数数组,其副作用是打印出元组的内容,逗号分隔,确保我们不会以额外的逗号开头。必须按顺序计算这四个表达式,以便保证按顺序打印元组内容。

初始(void)强制转换只是为了避免在打开所有警告时编译器应发出的未使用变量警告。数组初始化中的初始0处理元组为空的情况。

(os << (Is == 0? "" : ", ") << std::get<Is>(t))打印第 Is 个元素(前缀 ", " 表示Is > 0

然后,将结果转换为void以避免逗号运算符可能的重载。

(/*previous stuff*/, 0)...执行一系列 0

{0, /*previous stuff*/ }管理sizeof...(Is) == 0的情况

swallow /*previous stuff*/构建一个 0 的 int 数组。

void /*previous stuff*/:投掷至空化以避免警告。