【问题标题】:Don't print trailing delimiter stream_iterator C++不要打印尾随分隔符 stream_iterator C++
【发布时间】:2012-06-17 18:50:42
【问题描述】:

在您看来,在 C++11 中使用 std::ostream_iterator 打印到 std::cout 并避免打印尾随分隔符的最优雅的方式是什么?

我正在打印的对象有双向迭代器,但没有随机访问迭代器。

std::list<double> x{1,2,3,4,5,6};
std::copy(x.begin(), std::prev(x.end()),
                std::ostream_iterator<int>(std::cout, ",") );
if ( x.size() != 0 )
  std::cout << *(--x.end()) << std::endl;

【问题讨论】:

  • 最优雅”根据定义是主观的(阅读:非建设性的)。
  • @ildjarn 我已经编辑为“在你看来”,但我觉得你的评论有点迂腐:你真的不认为这是一个有用的问题吗?我会写“最简洁”而不是“最优雅”,但en.wikipedia.org/wiki/Kolmogorov_complexity 是不可计算的。 ; )
  • 没有优雅的方法可以做到这一点。您必须检查某些特殊情况。将其隐藏在函数中并继续。
  • 如果你已经有一个容器,你可以试试pretty printer
  • 你应该看看infix_iterator

标签: c++ iterator c++11 ostream


【解决方案1】:

这是我的最爱之一,但它没有使用std::ostream_iterator

#include <iterator>
#include <string>
#include <iosfwd>

template <class C>
auto
print(std::ostream& os, const C& c,
      const std::string& delim = std::string(", "),
      const std::string& open_brace = std::string("{"),
      const std::string& close_brace = std::string("}")
     ) -> decltype(std::begin(c), std::end(c), os)
{
    os << open_brace;
    auto i = std::begin(c);
    auto e = std::end(c);
    if (i != e)
    {
        os << *i;
        for (++i; i != e; ++i)
            os << delim << *i;
    }
    os << close_brace;
    return os;
}

#include <list>
#include <iostream>

int main()
{
    std::list<double> x{1,2,3,4,5,6};
    print(std::cout, x) << '\n';
}

{1, 2, 3, 4, 5, 6}

更新

Oliver 激起了我无法抗拒的挑战。 :-)

#include <iterator>
#include <string>
#include <iosfwd>

namespace my {

template <class C>
auto
print(std::ostream& os, const C& c,
      const std::string& delim = std::string(", "),
      const std::string& open_brace = std::string("{"),
      const std::string& close_brace = std::string("}")
     ) -> decltype(std::begin(c), std::end(c), os);

template <class C,
           typename std::enable_if
                    <
                       !std::is_same<C, std::string>::value,
                    bool>::type = false
         >
inline
auto
operator<< (std::ostream& os, const C& c) -> decltype(print(os, c))
{
    return print(os, c);
}

template <class C>
auto
print(std::ostream& os, const C& c,
      const std::string& delim,
      const std::string& open_brace,
      const std::string& close_brace
     ) -> decltype(std::begin(c), std::end(c), os)
{
    os << open_brace;
    auto i = std::begin(c);
    auto e = std::end(c);
    if (i != e)
    {
        os << *i;
        for (++i; i != e; ++i)
            os << delim << *i;
    }
    os << close_brace;
    return os;
}

}

#include <list>
#include <forward_list>
#include <iostream>

int main()
{
    std::forward_list<std::list<double>> x{{}, {3, 2, 1}, {1,2,3,4,5,6}};
    my::print(std::cout, x) << '\n';
}

{{}, {3, 2, 1}, {1, 2, 3, 4, 5, 6}}

这并不完美,但很有趣。 :-) 可能有更好的方法可以更忠实地传播自定义分隔符和大括号。

【讨论】:

  • +1 是否对此进行了调整,以便可以打印list&lt;list&lt;double&gt; &gt;?如果它们是容器,也许递归调用内部对象的 print?
  • 请问为什么decltype(std::begin(c), std::end(c), os) 是为了?返回值不能只是std::ostream&amp;吗?
  • @Oliver:是的,递归调用内部对象的 print 可能是要走的路。我只是为了好玩而拼凑了一个替代方案。
  • @HowardHinnant 超级酷!这将非常适合调试(我总是说服自己“只输入一次”嵌套的 for 循环。:)
  • @mfontanini:我想将print&lt;C&gt; 函数限制在std::begin(c)std::end(c) 合法的那些类型。这将使我能够稍后(例如)创建一个print(std::ostream&amp;, double) 重载,而不必担心对这个新重载的调用会被错误地路由到print(std::ostream&amp;, const C&amp;) 的容器版本。 IE。如果尾随返回中的 decltype 对于 C 的特定实例化无效,则该实例化将被 SFINAE 淘汰。
【解决方案2】:

只需将光标向后移动:

std::cout << "\b";

然后覆盖分隔符。

【讨论】:

    【解决方案3】:

    C++14 - 不幸的是需要输入/输出流:

    #include <algorithm>
    #include <iostream>
    #include <iterator>
    #include <sstream>
    #include <list>
    
    int main()
    {
        std::list<double> x{1,2,3,4,5,6};
        std::stringstream sstr;
        std::transform(x.begin(), x.end(), std::ostream_iterator<std::string>(sstr), [&](auto c){
            return (sstr.rdbuf()->in_avail() ? "," : "") + std::to_string(c);
        });
        std::cout << sstr.str() << std::endl;
    }
    

    live

    从 C++17 开始会有 ostream_joiner 来解决这个问题:

    #include <algorithm>
    #include <experimental/iterator>
    #include <iostream>
    #include <iterator>
    
    int main()
    {
        int i[] = {1, 2, 3, 4, 5};
        std::copy(std::begin(i),
                  std::end(i),
                  std::experimental::make_ostream_joiner(std::cout, ", "));
    }
    

    live

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-15
      • 1970-01-01
      • 1970-01-01
      • 2017-05-07
      相关资源
      最近更新 更多