使用折叠表达式打印所有可变参数,并在其间使用换行符

Tho*_*ire 10 c++ variadic-templates fold-expression c++17

C++ 17折叠表达式的经典示例是打印所有参数:

template<typename ... Args>
void print(Args ... args)
{
    (cout << ... << args);
}
Run Code Online (Sandbox Code Playgroud)

例:

print("Hello", 12, 234.3, complex<float>{12.3f, 32.8f});
Run Code Online (Sandbox Code Playgroud)

输出:

Hello12234.3(12.3,32.8)
Run Code Online (Sandbox Code Playgroud)

我想在输出中添加换行符.但是,我找不到一个好方法,到目前为止我找到的最好的方法:

template<typename ... Args>
void print(Args ... args)
{
    (cout << ... << ((std::ostringstream{} << args << "\n").str()));
}
Run Code Online (Sandbox Code Playgroud)

然而,这不是零开销,因为它ostringstream为每个参数构造临时值.

以下版本也不起作用:

(cout << ... << " " << args);

error: expression not permitted as operand of fold expression
Run Code Online (Sandbox Code Playgroud)

(cout << ... << (" " << args));

error: invalid operands to binary expression 
Run Code Online (Sandbox Code Playgroud)

我理解为什么最后两个版本不起作用.使用折叠表达式是否有更优雅的解决方案?

Vit*_*meo 11

更新:TC的评论提供了一个更好的解决方案:

template<typename ... Args>
void print(Args ... args)
{
    ((cout << args << '\n'), ...);
}
Run Code Online (Sandbox Code Playgroud)

您可以在逗号运算符上使用fold表达式:

template<typename ... Args>
void print(Args ... args)
{
    ([](const auto& x){ cout << x << "\n"; }(args), ...);
}
Run Code Online (Sandbox Code Playgroud)

用法:

int main()
{
    print("a", 1, 1000);
}
Run Code Online (Sandbox Code Playgroud)

一个

1

1000

(注意:这也会打印一个尾随换行符.)


说明:

  • [](const auto& x){ cout << x << "\n"; }是一个lambda给出x打印x'\n'.

  • [](const auto& x){ cout << x << "\n"; }(args)立即调用lambda 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>)
    
    Run Code Online (Sandbox Code Playgroud)

  • 为什么lambda?`((cout << args <<'\n'),...)` (6认同)

Yak*_*ont 6

repeat获取一个函数对象f,并返回一个新的函数对象.返回值在每个args上运行f.它f在每个args上"重复" .

template<class F>
auto repeat( F&& f ) {
  return [f=std::forward<F>(f)](auto&&...args)mutable{
    ( void(f(args)), ... );
  };
}
Run Code Online (Sandbox Code Playgroud)

使用:

repeat
( [](auto&&x){ std::cout << x << "\n"; } )
( args... );
Run Code Online (Sandbox Code Playgroud)

这使用折叠表达式,但只是间接表达.说实话,你可以用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)}; }
Run Code Online (Sandbox Code Playgroud)

然后我们像这样使用它:

(std::cout << ... << ostreamer([&](auto&& os){ os << " " << args;}));
Run Code Online (Sandbox Code Playgroud)

ostreamer接受一个功能对象.它返回一个重载的对象,<<当你在左边传递一个ostream时,它会用ostream调用函数对象.

没有创建临时流.

实例.