Range-based loop over an input stream

前端 未结 4 1004
醉梦人生
醉梦人生 2021-02-08 16:57

To iterate over an input stream, we would usually use a std::istream_iterator like so:

typedef std::istream_iterator input_iterato         


        
4条回答
  •  时光说笑
    2021-02-08 17:45

    I attempted to pursue the idea of specializing std::begin and std::end for classes derived from std::basic_istream (I'm not so great at this template metaprogramming business):

    namespace std
    {
      template 
      typename
      std::enable_if<
        std::is_base_of, C>::value,
        std::istream_iterator>::type
      begin(C& c)
      {
        return {c};
      }
    
      template 
      typename
      std::enable_if<
        std::is_base_of, C>::value,
        std::istream_iterator>::type
      end(C& c)
      {
        return {};
      }
    }
    

    Actually, it works pretty well. I didn't create versions that take const C& because I don't think it makes sense to extract from a const stream (and I had errors when I tried to do so). I'm also not sure if I can make this more move friendly. So now I can print out the contents of myfile like so::

    std::ifstream file("myfile");
    std::copy(begin(file), end(file), std::ostream_iterator(std::cout, " "));
    

    So these begin and end functions work as expected. However, it falls flat when used in a range-based for loop. std::basic_istream classes are derived from std::ios_base which already has a member called end (it's a flag for seeking within a stream). Once the range-based for loop finds this, it just gives up because it can't find a corresponding begin (not to mention that end is not the right kind of entity):

    main.cpp:35:33: error: range-based ‘for’ expression of type ‘std::basic_ifstream’ has an ‘end’ member but not a ‘begin’

    The only alternative that works in both situations, as others have mentioned, is to create a wrapper object. Unfortunately that end member in std::ios_base completely ruins any chance of implementing this in a nice way.

提交回复
热议问题