可变参数模板查询
Variadic template queries
我正在尝试理解下面的代码。直接从 Jason Turner youtube 视频复制
#include <iostream>
#include <sstream>
#include <vector>
template<typename ...T>
std::vector<std::string> print(const T& ...t)
{
std::vector<std::string> retval;
std::stringstream ss;
(void)std::initializer_list<int>{
(
ss.str(""),
ss << t,
retval.push_back(ss.str()),
0)...
};
return retval;
}
int main()
{
for( const auto &s : print("Hello", "World", 5.4, 1.1, 2.2) ) {
std::cout << s << "\n";
}
}
问题:
- 有人可以给出 initializer_list 中代码的扩展视图吗?我很难想象语句是如何扩展每个参数的? ss.str(""), ss << t 然后 push_back 是针对包中的每个参数发生还是只执行一次?我无法想象扩展的初始化列表会是什么样子?
- 为什么我们需要 initializer_list 末尾的虚拟“0”?如果我没有那个会怎样?
- 如何轻松查看共享代码中的...扩展?
T
和t
是参数包.
有两种使用包的主要方式:折叠表达式(在 C++17 和更新版本中)和常规包扩展。
A fold expression 看起来像这样:
((ss.str(""), ss << t, retval.push_back(ss.str())), ...);
折叠表达式为每个包元素重复其操作数,在属于每个参数的部分之间插入一些运算符(在本例中为 ,
)。上面的扩展为:
((ss.str(""), ss << t1, retval.push_back(ss.str())), // <-- Inserted commas
(ss.str(""), ss << t2, retval.push_back(ss.str())), // <--
(ss.str(""), ss << t3, retval.push_back(ss.str())));
常规扩展类似,只是它总是生成逗号,并且逗号不能是运算符(与数组初始值设定项或函数之间的分隔符相反)参数)。
例如如果您假设 (ss.str(""), ss << t, retval.push_back(ss.str()))...;
会像折叠表达式一样工作,那么它就不会工作,因为生成的逗号必须是一个运算符。
由于这个限制,在 C++17 之前人们使用虚拟数组(或像您的示例中的 initializer_list
s)。这是使用数组时的样子:
int dummy[] = {(ss.str(""), ss << t, retval.push_back(ss.str()), 0)...};
扩展为:
int dummy[] = {(ss.str(""), ss << t1, retval.push_back(ss.str()), 0),
(ss.str(""), ss << t2, retval.push_back(ss.str()), 0),
(ss.str(""), ss << t3, retval.push_back(ss.str()), 0)};
此处,数组(或initializer_list
)的大小与包的大小匹配。
,0
是必需的,因为每个数组元素都是一个int
,所以必须用int
.
初始化
我正在尝试理解下面的代码。直接从 Jason Turner youtube 视频复制
#include <iostream>
#include <sstream>
#include <vector>
template<typename ...T>
std::vector<std::string> print(const T& ...t)
{
std::vector<std::string> retval;
std::stringstream ss;
(void)std::initializer_list<int>{
(
ss.str(""),
ss << t,
retval.push_back(ss.str()),
0)...
};
return retval;
}
int main()
{
for( const auto &s : print("Hello", "World", 5.4, 1.1, 2.2) ) {
std::cout << s << "\n";
}
}
问题:
- 有人可以给出 initializer_list 中代码的扩展视图吗?我很难想象语句是如何扩展每个参数的? ss.str(""), ss << t 然后 push_back 是针对包中的每个参数发生还是只执行一次?我无法想象扩展的初始化列表会是什么样子?
- 为什么我们需要 initializer_list 末尾的虚拟“0”?如果我没有那个会怎样?
- 如何轻松查看共享代码中的...扩展?
T
和t
是参数包.
有两种使用包的主要方式:折叠表达式(在 C++17 和更新版本中)和常规包扩展。
A fold expression 看起来像这样:
((ss.str(""), ss << t, retval.push_back(ss.str())), ...);
折叠表达式为每个包元素重复其操作数,在属于每个参数的部分之间插入一些运算符(在本例中为 ,
)。上面的扩展为:
((ss.str(""), ss << t1, retval.push_back(ss.str())), // <-- Inserted commas
(ss.str(""), ss << t2, retval.push_back(ss.str())), // <--
(ss.str(""), ss << t3, retval.push_back(ss.str())));
常规扩展类似,只是它总是生成逗号,并且逗号不能是运算符(与数组初始值设定项或函数之间的分隔符相反)参数)。
例如如果您假设 (ss.str(""), ss << t, retval.push_back(ss.str()))...;
会像折叠表达式一样工作,那么它就不会工作,因为生成的逗号必须是一个运算符。
由于这个限制,在 C++17 之前人们使用虚拟数组(或像您的示例中的 initializer_list
s)。这是使用数组时的样子:
int dummy[] = {(ss.str(""), ss << t, retval.push_back(ss.str()), 0)...};
扩展为:
int dummy[] = {(ss.str(""), ss << t1, retval.push_back(ss.str()), 0),
(ss.str(""), ss << t2, retval.push_back(ss.str()), 0),
(ss.str(""), ss << t3, retval.push_back(ss.str()), 0)};
此处,数组(或initializer_list
)的大小与包的大小匹配。
,0
是必需的,因为每个数组元素都是一个int
,所以必须用int
.