模板循环遍历元组
template looping through tuple
我正在玩可变参数模板,我目前正在尝试为元组实现 operator<<
。
我尝试了以下代码,但它无法编译(GCC 4.9 with -std=c++11)。
template<int I, typename ... Tlist>
void print(ostream& s, tuple<Tlist...>& t)
{
s << get<I>(t) << ", ";
if(I < sizeof...(Tlist)){
print<I+1>(s,t);
}
}
template<typename ... Tlist>
ostream& operator<<(ostream& s, tuple<Tlist...> t)
{
print<0>(s,t);
return s;
}
错误信息很晦涩很长,但基本上是说没有匹配的get函数调用。有人可以向我解释为什么吗?
谢谢
编辑:
这是我正在使用的模板实例
auto t = make_tuple(5,6,true,"aaa");
cout << t << endl;
你必须使用专业化或SFINAE作为分支,即使它没有被生成实例化:
template<int I, typename ... Tlist>
void print(ostream& s, tuple<Tlist...>& t)
{
s << get<I>(t) << ", ";
if(I < sizeof...(Tlist)){
print<I+1>(s,t); // Generated even if I >= sizeof...(Tlist)
}
}
因此,如果 get<sizeof...(Tlist)>
不尽快产生错误,您将无限实例化 print
。
你可以不用递归写它:
template<std::size_t ... Is, typename Tuple>
void print_helper(std::ostream& s, const Tuple& t, std::index_sequence<Is...>)
{
int dummy[] = { 0, ((s << std::get<Is>(t) << ", "), 0)...};
(void) dummy; // remove warning for unused var
}
template<typename Tuple>
void print(std::ostream& s, const Tuple& t)
{
print_helper(s, t, std::make_index_sequence<std::tuple_size<Tuple>::value>());
}
if (blah) {
块 }
中的代码已编译并且必须有效,即使条件 blah
为假。
template<bool b>
using bool_t = std::integral_constant<bool, b>;
template<int I, typename ... Tlist>
void print(std::ostream& s, std::tuple<Tlist...> const& t, std::false_type) {
// no more printing
}
template<int I, typename ... Tlist>
void print(std::ostream& s, std::tuple<Tlist...> const& t, std::true_type) {
s << std::get<I>(t) << ", ";
print<I+1>(s, t, bool_t<((I+1) < sizeof...(Tlist))>{});
}
template<typename ... Tlist>
std::ostream& operator<<(std::ostream& s, std::tuple<Tlist...> const& t)
{
print<0>(s,t, bool_t<(0 < sizeof...(Tlist))>{});
return s;
}
应该可以。这里我们使用标签调度来控制我们递归调用的重载:如果 I
是元组的有效索引,第三个参数是 true_type
,如果不是,则为 false_type
。我们这样做而不是 if
语句。我们总是递归,但是当我们到达元组的末尾时,我们递归到终止重载。
顺便说一句,std
中定义的两种类型的重载 <<
是否符合标准是不明确的:这取决于 std::tuple<int>
是否是 "user defined type" or not,标准没有定义的条款。
最重要的是,最佳做法是在该类型的命名空间中为该类型重载运算符,以便可以通过 ADL 找到它。但是,根据标准,在 std
中重载 <<
是非法的(您不能向 std
中注入新的重载)。在某些情况下,结果可能会有些令人惊讶的行为,例如发现错误的重载或未找到重载。
我正在玩可变参数模板,我目前正在尝试为元组实现 operator<<
。
我尝试了以下代码,但它无法编译(GCC 4.9 with -std=c++11)。
template<int I, typename ... Tlist>
void print(ostream& s, tuple<Tlist...>& t)
{
s << get<I>(t) << ", ";
if(I < sizeof...(Tlist)){
print<I+1>(s,t);
}
}
template<typename ... Tlist>
ostream& operator<<(ostream& s, tuple<Tlist...> t)
{
print<0>(s,t);
return s;
}
错误信息很晦涩很长,但基本上是说没有匹配的get函数调用。有人可以向我解释为什么吗? 谢谢
编辑: 这是我正在使用的模板实例
auto t = make_tuple(5,6,true,"aaa");
cout << t << endl;
你必须使用专业化或SFINAE作为分支,即使它没有被生成实例化:
template<int I, typename ... Tlist>
void print(ostream& s, tuple<Tlist...>& t)
{
s << get<I>(t) << ", ";
if(I < sizeof...(Tlist)){
print<I+1>(s,t); // Generated even if I >= sizeof...(Tlist)
}
}
因此,如果 get<sizeof...(Tlist)>
不尽快产生错误,您将无限实例化 print
。
你可以不用递归写它:
template<std::size_t ... Is, typename Tuple>
void print_helper(std::ostream& s, const Tuple& t, std::index_sequence<Is...>)
{
int dummy[] = { 0, ((s << std::get<Is>(t) << ", "), 0)...};
(void) dummy; // remove warning for unused var
}
template<typename Tuple>
void print(std::ostream& s, const Tuple& t)
{
print_helper(s, t, std::make_index_sequence<std::tuple_size<Tuple>::value>());
}
if (blah) {
块 }
中的代码已编译并且必须有效,即使条件 blah
为假。
template<bool b>
using bool_t = std::integral_constant<bool, b>;
template<int I, typename ... Tlist>
void print(std::ostream& s, std::tuple<Tlist...> const& t, std::false_type) {
// no more printing
}
template<int I, typename ... Tlist>
void print(std::ostream& s, std::tuple<Tlist...> const& t, std::true_type) {
s << std::get<I>(t) << ", ";
print<I+1>(s, t, bool_t<((I+1) < sizeof...(Tlist))>{});
}
template<typename ... Tlist>
std::ostream& operator<<(std::ostream& s, std::tuple<Tlist...> const& t)
{
print<0>(s,t, bool_t<(0 < sizeof...(Tlist))>{});
return s;
}
应该可以。这里我们使用标签调度来控制我们递归调用的重载:如果 I
是元组的有效索引,第三个参数是 true_type
,如果不是,则为 false_type
。我们这样做而不是 if
语句。我们总是递归,但是当我们到达元组的末尾时,我们递归到终止重载。
顺便说一句,std
中定义的两种类型的重载 <<
是否符合标准是不明确的:这取决于 std::tuple<int>
是否是 "user defined type" or not,标准没有定义的条款。
最重要的是,最佳做法是在该类型的命名空间中为该类型重载运算符,以便可以通过 ADL 找到它。但是,根据标准,在 std
中重载 <<
是非法的(您不能向 std
中注入新的重载)。在某些情况下,结果可能会有些令人惊讶的行为,例如发现错误的重载或未找到重载。