Классический пример C ++ 17 выражений — это печать всех аргументов:
template<typename ... Args>
void print(Args ... args)
{
(cout << ... << args);
}
Пример:
print("Hello", 12, 234.3, complex<float>{12.3f, 32.8f});
Выход:
Hello12234.3(12.3,32.8)
Я хотел бы добавить новые строки в мой вывод. Тем не менее, я не могу найти хороший способ сделать это, лучшее, что я нашел до сих пор:
template<typename ... Args>
void print(Args ... args)
{
(cout << ... << ((std::ostringstream{} << args << "\n").str()));
}
Это, однако, не ноль накладных расходов, поскольку это создает временную ostringstream
за каждый аргумент.
Следующие версии также не работают:
(cout << ... << " " << args);
error: expression not permitted as operand of fold expression
А также
(cout << ... << (" " << args));
error: invalid operands to binary expression
Я понимаю, почему последние две версии не работают.
Есть ли более элегантное решение этой проблемы, используя выражения сгиба?
repeat
принимает функциональный объект f
и вернуть новый объект функции. Возвращаемое значение запускает f на каждом из своих аргументов. Это «повторяется» f
на каждом из его аргов.
template<class F>
auto repeat( F&& f ) {
return [f=std::forward<F>(f)](auto&&...args)mutable{
( void(f(args)), ... );
};
}
Использование:
repeat
( [](auto&&x){ std::cout << x << "\n"; } )
( args... );
Это использует выражения сгиба, но только косвенно. И, честно говоря, вы могли бы написать это в C ++ 14 (просто тело repeat
было бы уродливее).
Мы также могли бы написать стример, который работает с <<
чтобы сделать это «более встроенным» и использовать выражения сложения напрямую:
template<class F>
struct ostreamer_t {
F f;
friend std::ostream& operator<<( std::ostream& os, ostreamer_t&& self ) {
std::move(self).f(os);
return os;
}
};
template<class F>
ostreamer_t<F> ostreamer( F&& f ) { return {std::forward<F>(f)}; }
тогда мы используем это так:
(std::cout << ... << ostreamer([&](auto&& os){ os << " " << args;}));
ostreamer
принимает функциональный объект. Возвращает объект, который перегружается <<
такой, что когда вы передаете ему ostream слева, он вызывает объект функции с ostream.
Временный поток не создается.
Обновить: Агар Т.С.Комментарий ниже предоставил лучшее решение:
template<typename ... Args>
void print(Args ... args)
{
((cout << args << '\n'), ...);
}
Вы можете использовать сложить выражение над оператор запятой:
template<typename ... Args>
void print(Args ... args)
{
([](const auto& x){ cout << x << "\n"; }(args), ...);
}
Использование:
int main()
{
print("a", 1, 1000);
}
1
1000
(Примечание: также печатается завершающий символ новой строки.)
Объяснение:
[](const auto& x){ cout << x << "\n"; }
это лямбда, которая дана x
печать x
а также '\n'
,
[](const auto& x){ cout << x << "\n"; }(args)
немедленно вызывает лямбду с args
,
([](const auto& x){ cout << x << "\n"; }(args), ...)
это складчатое выражение над оператор запятой это расширяется следующим образом:
// (pseudocode)
[](const auto& x){ cout << x << "\n"; }(args<0>),
[](const auto& x){ cout << x << "\n"; }(args<1>),
[](const auto& x){ cout << x << "\n"; }(args<2>),
// ...
[](const auto& x){ cout << x << "\n"; }(args<N>)