【问题标题】:Anatomy of pretty print tuple漂亮打印元组的解剖
【发布时间】:2015-01-30 21:31:14
【问题描述】:

不久前,here 发布了打印 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 正在检查我们是否在头元素。

那么发生了什么?

【问题讨论】:

  • 代码从初始化列表构造(然后丢弃)int[] 数组,其中每个元素为 0,但打印元组的一个元素作为副作用(通过逗号运算符)。使用初始化列表只是为了进入包扩展可以工作的上下文。
  • 啊!所以swallow{...} 构造是 int[] 的初始化列表。乍一看我没听懂。

标签: c++ c++11 tuples variadic-templates pretty-print


【解决方案1】:

让我们看一个带有任意元组的例子,比如:

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 个 0s 组成的整数数组,其副作用是打印出以逗号分隔的元组的内容,确保我们不会以额外的逗号开头.四个表达式必须按顺序计算,这样可以保证元组内容按顺序打印。

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

【讨论】:

  • 我开始明白了。 void(...) 有什么作用?
  • @SalvadorGuzman 将os &lt;&lt; whatever 的结果转换为void,因此表达式(void(stuff), 0) 肯定会返回0。这是在有人决定为operator,() 编写一个重载而不是返回int 的情况下完成的。
  • 这更有意义。最后一个问题。那么 '...' 运算符是解包器吗?我可以指望它返回逗号分隔值吗?
  • @SalvadorGuzman 这不是运算符,但它确实扩展了参数包。阅读this reference,这是一个很好的介绍。
【解决方案2】:

(os &lt;&lt; (Is == 0? "" : ", ") &lt;&lt; std::get&lt;Is&gt;(t)) 打印Isth 元素(前缀", " 代表Is &gt; 0

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

(/*previous stuff*/, 0)...做了一系列的0

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

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

void /*previous stuff*/ :强制转换为 void 以避免警告。

【讨论】:

    猜你喜欢
    • 2021-05-18
    • 2021-06-22
    • 1970-01-01
    • 2016-07-07
    • 2014-05-19
    • 1970-01-01
    • 2011-03-21
    • 2018-10-04
    相关资源
    最近更新 更多