打印逗号与std :: vector分开的列表

Print comma separated list from std::vector

本文关键字:列表 vector 打印 std      更新时间:2023-10-16

我正在尝试从 std::vector<MyClass>打印单个细节的逗号分隔列表。到目前为止,我看到的最简单,最聪明的方法是使用

std::ostringstream ss;
std::copy(vec.begin(), vec.end() - 1, std::ostream_iterator<std::string>(ss, ", "))
ss << vec.back();

当我打印一个字符串向量时,

效果很好。但是,现在我试图打印有关MyClass的单个细节。我知道我可以做

之类的事情
(x.specific_detail for x in vec)

要获得我感兴趣的东西的发电机表达式。我想知道我是否可以在这里做类似的事情,或者是否卡住了

for (auto it = vec.begin(); it != vec.end(); ++it) {
    // Do stuff here
}

解决此问题的一种方法是:

std::string separator;
for (auto x : vec) {
  ss << separator << x.specific_detail;
  separator = ",";
}

一种相当简单且可重复使用的方法:

#include <vector>
#include <iostream>
template<class Stream, class T, class A>
Stream& printem(Stream&os, std::vector<T, A> const& v)
{
    auto emit = [&os, need_comma = false](T const& x) mutable
    {
        if (need_comma) os << ", ";
        os << x;
        need_comma = true;
    };
    for(T const& x : v) emit(x);
    return os;
}

int main()
{
    auto v = std::vector<int> { 1, 2, 3, 4 , 5 };
    printem(std::cout, v) << std::endl;
}

和另一种定义用于打印容器的可扩展协议的方式:

#include <vector>
#include <iostream>
template<class Container>
struct container_printer;
// specialise for a class of container
template<class T, class A>
struct container_printer<std::vector<T, A>>
{
    using container_type = std::vector<T, A>;
    container_printer(container_type const& c) : c(c) {}
    std::ostream& operator()(std::ostream& os) const 
    {
        const char* sep = "";
        for (const T& x : c) {
            os << sep << x;
            sep = ", ";
        }
        return os;
    }
    friend std::ostream& operator<<(std::ostream& os, container_printer const& cp)
    {
        return cp(os);
    }
    container_type c;
};
template<class Container>
auto print_container(Container&& c)
{
    using container_type = typename std::decay<Container>::type;
    return container_printer<container_type>(c);
}

int main()
{
    auto v = std::vector<int> { 1, 2, 3, 4 , 5 };
    std::cout << print_container(v) << std::endl;
}

...当然我们可以走得更远...

#include <vector>
#include <iostream>
template<class...Stuff>
struct container_printer;
// specialise for a class of container
template<class T, class A, class Separator, class Gap, class Prefix, class Postfix>
struct container_printer<std::vector<T, A>, Separator, Gap, Prefix, Postfix>
{
    using container_type = std::vector<T, A>;
    container_printer(container_type const& c, Separator sep, Gap gap, Prefix prefix, Postfix postfix) 
    : c(c)
    , separator(sep)
    , gap(gap)
    , prefix(prefix)
    , postfix(postfix) {}
    std::ostream& operator()(std::ostream& os) const 
    {
        Separator sep = gap;
        os << prefix;
        for (const T& x : c) {
            os << sep << x;
            sep = separator;
        }
        return os << gap << postfix; 
    }
    friend std::ostream& operator<<(std::ostream& os, container_printer const& cp)
    {
        return cp(os);
    }
    container_type c;
    Separator separator;
    Gap gap;
    Prefix prefix;
    Postfix postfix;
};
template<class Container, class Sep = char, class Gap = Sep, class Prefix = char, class Postfix = char>
auto print_container(Container&& c, Sep sep = ',', Gap gap = ' ', Prefix prefix = '[', Postfix postfix = ']')
{
    using container_type = typename std::decay<Container>::type;
    return container_printer<container_type, Sep, Gap, Prefix, Postfix>(c, sep, gap, prefix, postfix);
}

int main()
{
    auto v = std::vector<int> { 1, 2, 3, 4 , 5 };
    // json-style
    std::cout << print_container(v) << std::endl;
    // custom
    std::cout << print_container(v, " : ", " ", "(", ")") << std::endl;
    // custom
    std::cout << print_container(v, "-", "", ">>>", "<<<") << std::endl;
}

预期输出:

[ 1,2,3,4,5 ]
( 1 : 2 : 3 : 4 : 5 )
>>>1-2-3-4-5<<<

这是使用std::transform

的示例
#include <vector>
#include <string>
#include <iterator>
#include <algorithm>
#include <iostream>
int main()
{
    std::vector<std::string> strs = {"Testing", "One", "Two", "Three"};
    if (!strs.empty())
    {
        std::copy(std::begin(strs), std::prev(std::end(strs)), std::ostream_iterator<std::string>(std::cout, ", "));
        std::cout << strs.back();
    }
    std::cout << 'n';
    if (!strs.empty())
    {
        std::transform(std::begin(strs), std::prev(std::end(strs)), std::ostream_iterator<size_t>(std::cout, ", "),
                       [](const std::string& str) { return str.size(); });
        std::cout << strs.back().size();
    }
    std::cout << 'n';
}

输出:

Testing, One, Two, Three
7, 3, 3, 5

这是一个很小的简单范围库:

template<class It>
struct range_t {
  It b, e;
  It begin() const { return b; }
  It end() const { return e; }
  bool empty() const { return begin()==end(); }
  std::size_t size() const { return std::distance( begin(), end() ); }
  range_t without_front( std::size_t n = 1 ) const {
    n = (std::min)(size(), n);
    return {std::next(b, n), e};
  }
  range_t without_back( std::size_t n = 1 ) const {
    n = (std::min)(size(), n);
    return {b, std::prev(e, n)};
  }
  range_t only_front( std::size_t n = 1 ) const {
    n = (std::min)(size(), n);
    return {b, std::next(b, n)};
  }
  range_t only_back( std::size_t n = 1 ) const {
    n = (std::min)(size(), n);
    return {std::prev(end(), n), end()};
  }
};
template<class It>
range_t<It> range(It s, It f) { return {s,f}; }
template<class C>
auto range(C&& c) {
  using std::begin; using std::end;
  return range( begin(c), end(c) );
}

现在我们准备好了。

auto r = range(vec);
for (auto& front: r.only_front()) {
  std::cout << front.x;
}
for (auto& rest: r.without_front()) {
  std::cout << "," << rest.x;
}

现场示例。

现在您可以变得更加豪华。增强转换迭代器以及Boost范围,让您可以做类似于Python列表理解的事情。或c 2a。

的rangesv3库

编写转换输入迭代器并不难,它只是一堆样板。只需查看输入迭代器的公理,编写一种存储任意迭代器并将大多数方法转发到它的类型。

它还存储一些功能。在*->上,调用Derected Iterator上的功能。

template<class It, class F>
struct transform_iterator_t {
  using reference=std::result_of_t<F const&(typename std::iterator_traits<It>::reference)>;
  using value_type=reference;
  using difference_type=std::ptrdiff_t;
  using pointer=value_type*;
  using iterator_category=std::input_iterator_tag;
  using self=transform_iterator_t;
  It it;
  F f;
  friend bool operator!=( self const& lhs, self const& rhs ) {
    return lhs.it != rhs.it;
  }
  friend bool operator==( self const& lhs, self const& rhs ) {
    return !(lhs!=rhs);
  }
  self& operator++() {
    ++it;
    return *this;
  }
  self operator++(int) {
    auto r = *this;
    ++*this;
    return r;
  }
  reference operator*() const {
    return f(*it);
  }
  pointer operator->() const {
    // dangerous
    return std::addressof( **this );
  }
};
template<class F>
auto iterator_transformer( F&& f ) {
  return [f=std::forward<F>(f)](auto it){
    return transform_iterator_t<decltype(it), std::decay_t<decltype(f)>>{
      std::move(it), f
    };
  };
}
template<class F>
auto range_transfromer( F&& f ) {
  auto t = iterator_transformer(std::forward<F>(f));
  return [t=std::move(t)](auto&&...args){
    auto tmp = range( decltype(args)(args)... );
    return range( t(tmp.begin()), t(tmp.end()) );
  };
}

变压器的实时示例。

,如果我们添加--,我们甚至可以使用Ostream Iterator。

请注意,std::prev需要双向迭代器,该迭代器需要向前迭代概念,该迭代概念要求转换器返回实际参考,这是痛苦。

您可以使用已经拥有的确切代码,只需将您传递给std::ostream_iterator的类型更改为限制其输出:

class MyClassDetail {
    const MyClass &m_cls;
public:
    MyClassDetail(const MyClass &src) : m_cls(src) {}
    friend std::ostream& operator<<(std::ostream &out, const MyClassDetail &in) {
        return out << in.m_cls.specific_detail;
    }
};
std::copy(vec.begin(), vec.end()-1, std::ostream_iterator<MyClassDetail>(ss, ", "));
ss << MyClassDetail(vec.back());

实时演示

这是最终使用的

// assume std::vector<MyClass> vec
std::ostringstream ss;
std::for_each(vec.begin(), vec.end() - 1,
    [&ss] (MyClass &item) {
        ss << item.specific_detail << ", ";
    }
);
ss << vec.back().specific_detail;

您可以简单地使用完全相同的代码,但定义了操作员&lt;&lt;超载:

ostream &operator<<(ostream& out)
{
    out << m_detail;
}
相关文章: