Можно ли сложить только часть пакета с 17-кратными выражениями C ++?
Нет, выражение сгиба будет складываться по всей упаковке.Тем не менее, мы можем сделать несколько трюков для достижения того, что нам нужно.
Здесь ваша функция 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
a, b
a, b, c
Хитрость в том, что мы разбиваем пакет параметров на единичную «голову» (str
) и вариационный «хвост» (strs
).Таким образом, мы позволяем списку параметров функции вытащить первый элемент из пакета.(A lot метапрограммирования шаблонов в стиле C ++ 11 использовал этот трюк).
Другой подход - создать набор индексов 0, 1, ..., N длянаш пакет параметров и затем для нашей логики свертывания мы могли бы сделать что-то особенное для 0-го, N-го или даже произвольного элемента.Вы можете найти варианты этого подхода на вопросе pretty print tuple .В C ++ 20 благодаря лямбда-шаблонам в C ++ 20 мы можем переместить всю логику в один метод, например, так:
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)>{});
}
Этот уродливый синтаксис - это я создаю лямбду и вызываю ее в одном выражении.Возможно, вы можете сделать его более читабельным, вызывая его с помощью 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, до н.э., де, фг