如何使参数包成组折叠或成对折叠?


14
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);

Answers:


9

您可以使用折叠表达式!它不是最漂亮的*,但是比提供的所有非折叠解决方案都短:

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

我们在逗号运算符上执行折叠,其中每个操作数是一个args和交替令牌的输出,外加切换令牌索引(后两个与另一个逗号运算符组合)。

*对于熟悉折叠表达式(和逗号运算符)的读者来说,这可能是“最好的”代码,但是对于其他所有人来说,这简直是胡言乱语,因此请您自行判断是否要在代码库中强加它。


我想这也可以和布尔(如果仅需要配对)ala一起使用。:b ^ = true; 然后也许是时间运算符(b?“:'”,“:”'“)
darune

1
@darune当然,还有其他表达替换的方法。我决定将输出/替换逻辑与实际的标记值分开,该标记可以很好地完成该数组。我不喜欢索引时从bool到的隐式转换,int因此我使用了实际的方法int来切换状态。而prefix和postfix ++花费了额外的思维周期来验证(至少对我而言),而分隔1 - 符确实不会被误读。简而言之,我试图使它尽可能地可读,但这当然取决于个人喜好(或适用的样式指南)。max66压缩得更多。
Max Langhof

使用a std::array而不是本机数组似乎毫无意义。
Deduplicator

@Deduplicator我非常不同意,因为我发现它std::array<const char*, 2>比无限可读const char**。但是,这也是在围绕一些非常晦涩的语法的可读性方面的最佳成绩,您可以在自己的代码中使用它来进行处理。我所能做的就是为您提供我认为可读的数据点。
Max Langhof

9

遵循以下模式的几个辅助函数很容易做到这一点。

void helper() {}

template <class T1, class T2, class ... T>
void helper(T1 t1, T2 t2, T ... t)
{
     do_single_pair(t1, t2);
     helper(t...);
}

这不是折叠表达式,但最终结果是相同的。


模板递归深度是否与折叠表达式不同?还是一样?
darune

1
@darune折叠表达式没有固有的递归...折叠表达式只是形式上扩展为某个表达式(在可变参数模板的特定实例中)。
Max Langhof

6

我想您可以尝试使用索引和三元运算符。

如下

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();
 }

@MaxLanghof这具有易于扩展到更多分隔符的优点(?)。
Deduplicator

@Deduplicator我不明白您指的是什么?你可以解释吗?
Max Langhof

@Deduplicator-对我来说不清楚,“扩展到更多分隔符”是什么意思……无论如何……这种解决方案与公认的解决方案非常相似;我不认为它或多或少具有可扩展性。我想这有点轻(小!也许编译器以相同的方式进行优化),因为避免使用a std::array(无论如何都是light类),但是(所以我认为最好是接受的答案)可读性较差。
max66

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;
}

这要求所有人都args可以转换为std::string
胡桃木

@核桃,是正确的。如果这不是必需条件,那么您将不得不折叠表达式/递归
Mattias De Charleroy

1

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...));
}

演示版

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.