提问人:darune 提问时间:12/4/2019 最后编辑:TylerHdarune 更新时间:12/5/2019 访问量:814
如何对参数包进行分组或成对折叠?
How to make grouped or paired fold of parameter pack?
问:
template<class Msg, class... Args>
std::wstring descf(Msg, Args&&... args) {
std::wostringstream woss;
owss << Msg << ". " << ... << " " << args << ": '" << args << "' ";//not legal at all
//or
owss << Msg << ". " << args[0] << ": '" << args[1] << "' " << args[2] << ": '" << args[3] << "' "; //... pseudo code, and so on...
}
我知道我可以只使用对列表或类似的东西,但我对如何在保持函数语法的同时做到这一点感兴趣:
const auto formatted = descf(L"message", "arg1", arg1, "arg2", arg2);
答:
9赞
n. m. could be an AI
12/4/2019
#1
使用遵循以下模式的几个帮助程序函数,这很容易。
void helper() {}
template <class T1, class T2, class ... T>
void helper(T1 t1, T2 t2, T ... t)
{
do_single_pair(t1, t2);
helper(t...);
}
这不是折叠表达式,但最终结果是相同的。
评论
0赞
darune
12/4/2019
模板递归深度会与折叠表达式不同吗?还是会是一样的
1赞
Max Langhof
12/4/2019
@darune 折叠表达式没有固有的递归......折叠表达式只是正式扩展到某个表达式(在可变参数模板的特定实例化中)。
2赞
Mattias De Charleroy
12/4/2019
#2
下面的代码应该可以解决问题。参数包在初始值设定项列表中展开。
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
template <typename...Args>
std::string descf(std::string msg, Args &&... args)
{
auto argumentsVector = std::vector<std::string>{args...};
std::stringstream ss;
ss << msg << ". ";
for (auto i = std::size_t{0}; i < argumentsVector.size() - 1; ++i)
ss << argumentsVector[i] << ": '" << argumentsVector[i+1] << "' ";
auto result = ss.str();
if (!argumentsVector.empty())
result.pop_back();
return result;
}
int main()
{
std::cout << descf("message", "arg1", "1", "arg2", "2") << std::endl;
}
评论
0赞
walnut
12/4/2019
这要求所有人都可以转换为 s。args
std::string
0赞
Mattias De Charleroy
12/4/2019
@walnut,这是正确的。如果这不是必需的,那么你必须对折叠表达式/递归产生结果
9赞
Max Langhof
12/4/2019
#3
您可以使用折叠表达式!它不是最漂亮的*,但它比所有提出的非折叠解决方案都要短:
template<class T, class ... Args>
std::wstring descf(T msg, Args&&... args) {
std::wostringstream owss;
owss << msg << ". ";
std::array<const char*, 2> tokens{": '", "' "};
int alternate = 0;
((owss << args << tokens[alternate], alternate = 1 - alternate), ...);
return owss.str();
}
带有示例输出的演示:https://godbolt.org/z/Gs8d2x
我们对逗号运算符进行折叠,其中每个操作数是 1 和交替标记的输出,加上切换标记索引(后两者与另一个逗号运算符组合)。args
*对于熟悉折叠表达式(和逗号运算符)的读者来说,这可能是“最好的”代码,但对于其他人来说,这完全是胡言乱语,所以使用你自己的判断是否要对你的代码库施加这种影响。
评论
0赞
darune
12/4/2019
我想这也适用于布尔(如果只需要配对)唉。: b ^= 真;然后也许是 tenary 运算符 (b ?": '", " : "' ")
1赞
Max Langhof
12/4/2019
@darune 当然,还有其他方式可以表达这种交替。我决定将输出/交替逻辑与实际的标记值分开,数组很好地完成了这一点。我不喜欢索引时从 to 的隐式转换,所以我用实际来切换状态。前缀与后缀需要额外的心理周期来验证(至少对我来说),而分离不会真正被误读。简而言之,我试图使它尽可能具有可读性,但这当然取决于个人品味(或适用的风格指南)。MAX66 更浓缩了它。bool
int
int
++
1 -
0赞
Deduplicator
12/4/2019
使用一个而不是一个原生数组似乎是一个毫无意义的复杂问题。std::array
0赞
Max Langhof
12/4/2019
@Deduplicator我强烈反对,因为我发现它比 .但同样,这是我围绕一些非常晦涩的语法的可读性的最佳机会,您可以在自己的代码中使用它。我所能做的就是给你我认为可读的数据点。std::array<const char*, 2>
const char**
6赞
max66
12/4/2019
#4
我想你可以尝试使用索引和三元运算符。
内容如下
template <typename ... Args>
std::wstring descf (std::wstring const & Msg, Args && ... args)
{
std::wostringstream woss;
int i = 0;
((woss << Msg << ". "), ... ,(woss << args << (++i & 1 ? ": '" : "' ")));
return woss.str();
}
评论
0赞
Deduplicator
12/4/2019
@MaxLanghof 这样做的优点是易于扩展到更多分离器。
0赞
Max Langhof
12/4/2019
@Deduplicator我不明白你指的是什么?你能解释一下吗?
0赞
max66
12/5/2019
@Deduplicator - 我不清楚你说的“扩展到更多分隔符”是什么意思......无论如何。。。此解决方案与公认的解决方案非常相似;我不认为它或多或少是可扩展的。我想这有点(一点点!也许编译器以同样的方式优化)更轻,因为避免使用(无论如何,这是一个轻量级类)但(所以我认为更可取的是公认的答案)可读性较差。std::array
1赞
Jarod42
12/4/2019
#5
跟:std::index_sequence
template <class Msg, class... Pairs>
std::wstring descf_pair(const Msg& msg, const Pairs&... pairs)
{
std::wstringstream woss;
woss << msg << ". ";
auto sep = L"";
((woss << sep << std::get<0>(pairs) << L": '"
<< std::get<1>(pairs) << L"'", sep = L" "), ...);
return woss.str();
}
template <class Msg, std::size_t... Is, class Tuple>
decltype(auto) descf_impl(const Msg& msg, std::index_sequence<Is...>, Tuple&& t)
{
return descf_pair(msg, std::tie(std::get<2 * Is>(t), std::get<2 * Is + 1>(t))...);
}
template <class Msg, typename ... Ts>
std::wstring descf(const Msg& msg, const Ts&... ts)
{
static_assert(sizeof...(Ts) % 2 == 0);
return descf_impl(msg,
std::make_index_sequence<sizeof...(Ts) / 2>(),
std::tie(ts...));
}
上一个:编译时检查和运行时检查“同时”
下一个:升级后内环性能下降的原因是什么?
评论