使用折叠表达式打印所有带有换行符的可变参数
Using fold expressions to print all variadic arguments with newlines inbetween
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
我明白为什么最后两个版本不起作用。
这个问题有没有更优雅的解决方案,使用折叠表达式?
更新:T.C.下面的评论提供了更好的解决方案:
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);
}
a
1
1000
(注意:这也会打印尾随换行符。)
解释:
[](const auto& x){ cout << x << "\n"; }
是给定 x
打印 x
和 '\n'
.
的 lambda
[](const auto& x){ cout << x << "\n"; }(args)
立即调用带有 args
的 lambda。
([](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>)
repeat
接受一个函数对象 f
,return 接受一个新的函数对象。 return 值在其每个参数上运行 f。它在每个参数上 "repeats" 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
的主体会更丑)。
我们也可以编写一个与 <<
一起工作的流媒体来做到这一点 "more inline" 并直接使用折叠表达式:
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
接受一个函数对象。它 return 是一个重载 <<
的对象,这样当您向它传递左侧的 ostream 时,它会调用带有 ostream 的函数对象。
没有创建临时流。
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
我明白为什么最后两个版本不起作用。 这个问题有没有更优雅的解决方案,使用折叠表达式?
更新:T.C.下面的评论提供了更好的解决方案:
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);
}
a
1
1000
(注意:这也会打印尾随换行符。)
解释:
[](const auto& x){ cout << x << "\n"; }
是给定x
打印x
和'\n'
. 的 lambda
[](const auto& x){ cout << x << "\n"; }(args)
立即调用带有args
的 lambda。([](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>)
repeat
接受一个函数对象 f
,return 接受一个新的函数对象。 return 值在其每个参数上运行 f。它在每个参数上 "repeats" 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
的主体会更丑)。
我们也可以编写一个与 <<
一起工作的流媒体来做到这一点 "more inline" 并直接使用折叠表达式:
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
接受一个函数对象。它 return 是一个重载 <<
的对象,这样当您向它传递左侧的 ostream 时,它会调用带有 ostream 的函数对象。
没有创建临时流。