2014-12-02 165 views
5

前段时间,打印出std :: tuple的解决方案发布为here。大多数情况下,我会得到所发生的事情。尽管我很难理解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正在检查以查看我们是否处于头部元素。

那么这是怎么回事?

+5

该代码从初始值设定项列表中构造(然后抛出)一个int []数组,其中每个元素为0,但打印元组的一个元素作为副作用(通过逗号运算符)。初始化程序列表的使用只是为了进入包扩展工作的上下文。 – 2014-12-02 15:20:41

+0

啊!所以'swallow {...}'构造是int []的初始化列表。乍看之下我没有明白。 – sguzman 2014-12-02 17:14:33

回答

9

让我们通过一个例子用任意元组,说:

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秒的整数数组,印刷出来的元组的内容的副作用,用逗号分隔,确保我们不以逗号开头。四个表达式必须按顺序评估,以保证元组内容按顺序打印。

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

+0

我开始明白了。 void(...)'做什么? – sguzman 2014-12-02 17:24:16

+1

@SalvadorGuzman将'os << whatever'的结果转换为'void',以便表达式'(void(stuff),0)'绝对返回'0'。这是因为有人决定为'operator,()'写一个重载而不是返回int的重载。 – Barry 2014-12-02 17:33:01

+0

这更有意义。最后一个问题。那么'...'运算符是一个解包器吗?我可以依靠它来返回逗号分隔值吗? – sguzman 2014-12-02 17:41:09

1

(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*/:投射失效以避免警告。