12

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 

我明白为什么最后两个版本不起作用。使用折叠表达式是否有更优雅的解决方案来解决这个问题?

4

2 回答 2

18

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

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"; }是一个 lambda,它给出了xprintx'\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>)
    
于 2017-03-28T12:57:26.610 回答
7

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 调用函数对象。

没有创建临时流。

活生生的例子

于 2017-03-28T13:07:39.197 回答