提问人:alexpanter 提问时间:11/10/2023 更新时间:11/10/2023 访问量:77
嵌套 std::tuple 的 C++17 递归扁平化
C++17 recursive flattening of nested std::tuple
问:
我有一个函数,它应该从可变参数模板表达式构造一个。该函数如下所示(包括注释外的失败尝试):unpack
std::tuple
template<typename T, typename... Ts>
std::tuple<T,Ts...> unpack(std::stringstream& ss)
{
T t{};
WriteTypeStruct ret{};
if (writeType(ret, ss, t); (!ss || ret.err)) {
throw std::runtime_error(ret.msg);
}
//std::tuple args = {extract<std::decay_t<Ts>>(ss)...};
//std::tuple args = unpack<typename std::decay_t<Ts>...>(ss);
//return {t, unpack<Ts>(ss)...};
//return std::make_tuple(t, unpack<Ts>(ss)...);
//std::tuple<Ts...> unpacked = {unpack<Ts>(ss)...};
//return std::make_tuple(t, unpacked);
//return std::tuple_cat(std::tuple<T>(t), std::make_from_tuple(unpack<Ts>(ss)...));
return std::make_tuple(t, std::make_from_tuple<Ts...>(unpack<Ts>(ss)...)); // <-- cannot compile!
}
该函数是一系列模板化的重载函数,我用它来检查是否可以从 .此函数将一些返回值放在 中,其中包含一个布尔值和一个错误消息的字符串。writeType
std::stringstream
WriteTypeStruct
顶级域名;我正在为命令控制台编写一个基于字符串的解释器。
这里提供了该问题的完整演示:如何为基于文本的命令控制台存储参数化强类型函数,特别是@jarod42的答案。我正在使用这个函数而不是.unpack
extract
关于如何展平 here,有一个很好的解释,但我希望这种代码可以通过首先不创建嵌套元组来避免。std::tuple
但我似乎无法弄清楚正确的语法。
答:
我会从反序列化开始:
template<class T>
T deserialize(std::stringstream& ss) {
T t{};
WriteTypeStruct ret{};
if (writeType(ret, ss, t); (!ss || ret.err)) {
throw std::runtime_error(ret.msg);
}
return t;
}
它的语义非常简单。
template<class...Ts>
std::tuple<Ts...> unpack(std::stringstream& ss) {
return std::tuple<Ts...>{ unpack<Ts>(ss)... };
}
这些内部的评估顺序是有保证的。(注意:这是因为我使用了 ctor,而不是函数调用。程序员可以很容易地对上述内容进行“无所事事”的更改,并以一种取决于您使用的编译器的方式可怕地破坏它。{}
{}
您的大多数尝试都存在一个问题,即它们处理不好(终止情况)。unpack
unpack<>
调用而不是的那些(即,保证通过 1,并且在 0 处消除解包调用)最终会得到 1 层太多的元组,是你用元组包装的元组包。unpack<Ts>...
unpack<Ts...>
unpack<Ts>...
return std::tuple_cat(std::tuple<T>(t), std::make_from_tuple(unpack<Ts>(ss)...));
这很接近。要修复它,请执行以下操作:
return std::tuple_cat(std::tuple<T>(t), unpack<Ts>(ss)...);
只需去除 S 上多余的一层; 已返回一个元组。然后,我们 ,它支持任意数量的元组。tuple
unpack
unpack
tuple_cat
我们可以尝试优化它:
return std::tuple_cat(std::tuple<T>(t), unpack<Ts...>(ss));
但这遇到了空洞的问题。unpack<>
我在这里遇到的一个问题是,你把所有东西都放到一个元组里,只是为了把它再次取出。
空的也可以处理。添加之前:unpack<>
unpack
template<bool empty_pack=true>
std::tuple<> unpack(std::stringstream& ss) { return {}; }
然后应该调用该重载。unpack<>
但是,我发现所有这些都不如从元组版本调用的 1 元素版本优雅。
最后,我们可以编写一个元组扁平化函数,该函数接受任何一组参数,如果其中任何一个是元组,则将其扁平化,并返回融合列表。我个人认为这个功能很诱人,也是一个坏主意,因为它会立即“解决”一个问题,并在以后导致棘手的问题。
这有点像编写一个函数,该函数取消转义文本,但取消转义任意数量的层,直到不再有转义序列。或者一个逃避文本,但拒绝逃避任何已经逃脱的东西的人。
你把它扔进一个业务逻辑链中,它“解决”了一个不均匀转义或未转义的数据问题......并以无法修复的方式破坏事物。
评论
T
Ts...