[英]Is it possible to fold only part of the pack with C++17 fold expressions?
我試圖弄清楚如何使用 C++17 折疊表達式僅折疊可變參數模板包的一部分。 假設我想創建一個編譯時“分隔字符串”,如"str1_str2_str3_....."
使用這樣的代碼可以很容易地完成(只是一個例子):
std::string result;
template<class... Strings>
ConcatString(Strings&... strs)
{
(ConcatString(strs), ...);
}
template <class String>
void ConcatString(String& str)
{
result += str + "_"
}
然后我們可以這樣執行
std::string s1 = "s1", s2 = "s2", s3 = "s3";
ConcatString(s1, s2, s3);
// result == "s1_s2_s3_"
如您所見,最后一個分隔符有問題。 如果沒有運行時檢查,有什么辦法可以避免這個問題? 我可以想象的一種解決方案是僅折疊 (N - 1) 個 args 並“手動”連接最后一個。
您可以遞歸調用ConcatString
並使用 constexpr if 以避免運行時檢查
template<typename String, typename... Strings>
void ConcatString(String& str, Strings&... strs) {
if constexpr(sizeof...(strs) == 0) {
result += str;
} else {
result += str + "_";
ConcatString(strs...);
}
}
是否可以使用 C++17 折疊表達式僅折疊部分包裝?
不,折疊表達式將折疊整個包。 但是,我們可以做一些技巧來實現我們所需要的。
在這里,您的Concat
function 可以簡化為使用單個二進制折疊。
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;
}
Output:
一個
一,乙
a,b,c
這里的技巧是我們將參數包拆分為一個單數“頭”( str
)和一個可變參數“尾”( strs
)。 通過這種方式,我們讓 function 參數列表將第一個元素從包中拉出。 (很多C++11 風格的模板元編程都使用了這個技巧)。
另一種方法是為我們的參數包創建一組索引 0、1、...、N,然后對於我們的折疊邏輯,我們可以為第 0、第 N 甚至任意元素做一些特殊的事情。 您可以在漂亮的打印元組問題上找到這種方法的變體。 在 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;
會給我們一個 output 像
a,bc,de,fg
怎么樣:
template<typename Last>
std::string concat(Last last) {
return last;
}
template<typename First, typename ...Rest>
std::string concat(First first, Rest ...rest) {
return first + '_' + concat(rest...);
}
std::string s1{"foo"}, s2{"bar"}, s3{"baz"};
std::cout << concat(s1, s2, s3); // Prints "foo_bar_baz"
編輯:我使 function 更簡單:
template <typename First, typename... Rest>
std::string concat(First first, Rest... rest) {
return (first + ... + ('_' + rest));
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.