是否可以使用 C++17 折叠表达式仅折叠包的一部分?
不,折叠表达式将折叠整个包。但是,我们可以做一些技巧来实现我们需要的。
在这里,您的Concat 函数可以简化为使用单个二进制折叠。
template<class String, class... Strings>
std::string Concat(const std::string& delimiter, const String& str, const Strings&... strs)
{
return (str + ... + (delimiter + strs));
}
用法:
int main()
{
std::cout << Concat(",", "a") << std::endl;
std::cout << Concat(",", "a", "b") << std::endl;
std::cout << Concat(",", "a", "b", "c") << std::endl;
}
输出:
一个
a,b
a,b,c
这里的技巧是我们将参数包拆分为一个单数“头”(str)和一个可变参数“尾”(strs)。通过这种方式,我们让函数参数列表从包中提取第一个元素。 (很多 C++11 风格的模板元编程使用了这个技巧)。
另一种方法是为我们的参数包创建一组索引 0、1、...、N,然后对于我们的折叠逻辑,我们可以为第 0、第 N 甚至任意元素做一些特殊的事情.您可以在 pretty print tuple 问题上找到这种方法的各种变体。
在 C++20 中,由于 C++20 中的模板 lambda,我们可以将所有逻辑移动到一个方法中,如下所示:
template<class... Strings>
std::string Concat(const std::string& delimiter, const Strings&... strs)
{
return [&delimiter]<class Tup, size_t... I> (const Tup& tuple, std::index_sequence<I...>)
{
return (std::string{} + ... + (I == 0 ? std::get<I>(tuple) : delimiter + std::get<I>(tuple)));
}(std::tie(strs...), std::make_index_sequence<sizeof...(strs)>{});
}
那个丑陋的语法是我创建了一个 lambda 并在一个语句中调用它。您可以改为通过std::invoke 调用它使其更具可读性。
请注意,我们使用索引检查是否打印分隔符。
让我们使用索引检查技巧,只用分隔符连接彼此:
template<class... Strings>
std::string ConcatEveryOther(const std::string& delimiter, const Strings&... strs)
{
return [&delimiter]<class Tup, size_t... I> (const Tup& tuple, std::index_sequence<I...>)
{
return (std::string{} + ... + (I % 2 == 0 ? std::get<I>(tuple) : delimiter + std::get<I>(tuple)));
}(std::tie(strs...), std::make_index_sequence<sizeof...(strs)>{});
}
现在std::cout << ConcatEveryOther(",", "a", "b", "c", "d", "e", "f", "g") << std::endl; 会给我们一个像
这样的输出
a,bc,de,fg