【问题标题】:Is it possible to insert extra operation in fold expression?是否可以在折叠表达式中插入额外的操作?
【发布时间】:2018-11-08 22:16:49
【问题描述】:

在 C++17 中,折叠表达式是可用的,所以要打印参数,我们可以使用

#define EOL '\n'

template<typename ...Args>
void output_argus(Args&&... args) 
{
    (cout << ... << args) << EOL;
}


int main()
{
    output_argus(1, "test", 5.6f);
}

有输出
1test5.6

如果我想使用折叠表达式在每个元素上附加一个额外的字符 '\n' 来获得以下结果怎么办?

1
test
5.6

这可能吗?如果是,如何?

【问题讨论】:

    标签: c++ templates c++17 variadic-templates fold-expression


    【解决方案1】:

    如果我想使用折叠表达式为每个元素附加一个额外的字符 '\n' 来获得以下结果怎么办?

    您可以使用逗号运算符的强大功能

     ((std::cout << args << std::endl), ...);
    

    或者,按照 Quentin 的建议(谢谢)和您的要求,您可以简单地使用 \n 而不是 std::endl(以避免多次刷新流)

     ((std::cout << args << '\n'), ...); 
    

    【讨论】:

    • std::endl 完全是矫枉过正,OP 只想要一个换行符。
    【解决方案2】:

    这是@n.m. 的解决方案,没有粗鲁的全球贪婪operator&lt;&lt;

    template<class Os>
    struct chain_stream {
      Os& stream;
      template<class Rhs,
        std::enable_if_t<std::is_same_v<Os&, decltype(std::declval<Os&>() << std::declval<Rhs>())>, bool> =true
      >
      friend chain_stream<Os> const& operator<<( chain_stream<Os> const& os, Rhs&& rhs ) {
        os.stream << std::forward<Rhs>(rhs);
        return os;
      }
      // iomanipulator:
      friend chain_stream<Os> const& operator<<( chain_stream<Os> const& os, Os&(*rhs)(Os&) ) {
        os.stream << rhs;
        return os;
      }
      template<class Rhs,
        std::enable_if_t<
          std::is_same_v< std::result_of_t< Rhs&&(chain_stream const&) >, void >
          || std::is_same_v< std::result_of_t< Rhs&&(chain_stream const&) >, Os& >,
        bool> =true
      >
      friend chain_stream<Os> const& operator<<( chain_stream<Os> const& os, Rhs&& rhs ) {
        std::forward<Rhs>(rhs)( os );
        return os;
      }
    };
    

    现在我们可以这样做了:

     (chain_stream{std::cout} << ... << [&](auto& x){x << args << '\n';});
    

    它有效。

    【讨论】:

      【解决方案3】:

      我知道逗号运算符可能是最简单的方法,但为了完整起见,这是我想出的,主要是因为我想炫耀我对 iomanip 的小概括。标准库 iomanips 是函数。有一个采用函数指针的&lt;&lt; 重载。我将其扩展到通过引用获取和返回流的任意可调用对象。

      template <class Stream, class Func>
      auto operator << (Stream& s, Func f) -> 
              std::enable_if_t<std::is_same_v<decltype(f(s)), Stream&>, Stream&>
      {
          return f(s);
      }
      

      使用我们工具箱中的这个小工具,很容易编写一个折叠表达式,它可以做任何我们想做的事情。

      template<typename ...Args>
      void output_args(Args&&... args)
      {
           (std::cout << ... << [&](auto& x)->auto&{return x << args << '\n';});
      }
      

      这种技术可以用于我们需要捕获折叠表达式的值而不是其副作用的场景。在这种情况下,逗号运算符的用处不大。

      【讨论】:

      • 免费的贪婪&lt;&lt; 运营商似乎很粗鲁。相反,我会标记其中一种类型(函数或流)。
      • @Yakk-AdamNevraumont 这个想法是让函数对象像普通的旧函数一样工作。普通的旧函数已经可以用作 io 操纵器。
      • 是的,但它们的 &lt;&lt; 重载具有特定类型。你的将处理与 ostreams 完全无关的事情。可能是偶然的,在最坏的情况下,由于非 sfinae 错误导致构建中断。
      • @Yakk-AdamNevraumont 是的,很容易将其限制为 ostreams,这里没有这样做以减少冗长。
      猜你喜欢
      • 2021-01-19
      • 2020-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多