代码之家  ›  专栏  ›  技术社区  ›  Nick Chapman

从std::vector打印逗号分隔的列表[重复]

  •  6
  • Nick Chapman  · 技术社区  · 7 年前

    我正试图打印一个逗号分隔的列表,其中包含来自 std::vector<MyClass>

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

    当我打印字符串向量时,效果很好。然而,现在我试图打印一个关于 MyClass . 我知道在Python中我可以做类似的事情

    (x.specific_detail for x in vec)
    

    为了得到我感兴趣的东西的生成器表达式。我想知道我是否可以在这里做类似的事情,或者我是否一直在做

    for (auto it = vec.begin(); it != vec.end(); ++it) {
        // Do stuff here
    }
    
    7 回复  |  直到 7 年前
        1
  •  22
  •   Chris Drew    7 年前

    解决这个问题的一种方法是:

    std::string separator;
    for (auto x : vec) {
      ss << separator << x.specific_detail;
      separator = ",";
    }
    
        2
  •  3
  •   Richard Hodges    7 年前

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

    #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<<<
    
        3
  •  2
  •   Fred Larson    7 年前

    下面是一个使用 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
    
        4
  •  2
  •   Yakk - Adam Nevraumont    7 年前

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

    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;
    }
    

    Live example

    现在你可以更喜欢了。boost transform迭代器与boost range一起,可以实现类似于python中列表理解的功能。或C++2a的Rangesv3库。

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

    * -> ,调用解引用迭代器上的函数。

    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()) );
      };
    }
    

    Live example of transformer

    And if we add -- we can even use ostream iterator .

    请注意 std::prev 需要双向迭代器,这需要前向迭代器概念,这需要转换迭代器返回实际引用,这是一个难题。

        5
  •  1
  •   Remy Lebeau    7 年前

    您可以使用已经拥有的精确代码,只需更改传递给的类型 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());
    

    Live demo

        6
  •  0
  •   Nick Chapman    7 年前

    // 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;
    
        7
  •  -3
  •   Daniel Duvilanski    7 年前

    您可以简单地使用完全相同的代码,但要定义一个运算符<&书信电报;超载:

    ostream &operator<<(ostream& out)
    {
        out << m_detail;
    }