7
votes

I am try to create an iterable type which receives a template argument of a specific duration type, say std::seconds , std::hours , etc., and I want it to receive as argument 2 values that represent time_points of the specified duration, and be able to use such a construct in a range based for loop by increasing the current time_point by a unit of that duration or maybe of a specified duration, something like the following:

DateRange<std::seconds> dr(now() , 50);
for(auto d : dr){
 // do something at unit time
}

I have tried to implement it this way

   using namespace std::chrono;
   template<typename Duration , typename Clock_t = high_resolution_clock, 
   typename Time_type = time_point<Clock_t, typename Duration> , typename 
   Time_pointer = Time_type* >
 class DateRange {
   using Time_type_t = typename Time_type::duration;
 public:
   DateRange(Time_type_t start, Time_type_t end) :
    m_begin(start),
    m_end(end)
   {

   }
   DateRange(Time_type_t end):
    m_begin(Clock_t::now())

   {

   }
   Time_pointer begin(){
    return &m_begin;
   }
   Time_pointer end() {
    return &m_end;
    }

    Time_pointer operator++(){
    present +=Duration(1);
    return present_point;
   }

 Time_type operator*(){
    return present;
  }

private:
Time_type m_begin;
Time_type m_end;
Time_type present;
Time_pointer present_point = &present;
Clock_t l_clock;
};
int main()
{
DateRange<seconds> dr(40s);
dr.operator++();
    std::cout << (*dr).time_since_epoch().count();
 }

'std::chrono::time_point::time_point(std::chrono::time_point &&)': cannot convert argument 1 from 'std::chrono::steady_clock::time_point' to 'const _Duration &' DateRange at line 19

2
What is your issue ? - Jarod42
Consider migrating this question to Code Review. - D Drmmr
you seem to be trying to define a range (begin and end) and an iterator (operators ++ and *) in the same class, which is confusing at best - kmdreko
a range-based for loop gets begin and end iterators from the range expression (dr in your case). both behaviors could theoretically be defined in the same class, but that is not how it is intended - kmdreko
@kmdreko FWIW, fs::directory_iterator does a similar thing. So it's not completely uncommon. - HolyBlackCat

2 Answers

3
votes

Range-based for loops (for ( range_declaration : range_expression ) loop_statement) are syntactic sugar for (in your case) this:

{
    auto && __range = range_expression ;
    auto __begin = __range.begin();
    auto __end = __range.end();
    for ( ; __begin != __end; ++__begin)
    {
        range_declaration = *__begin;
        loop_statement
    }
} 

Without concerning ourselves with the finer details of this (or how it changed throughout C++ versions), this explains why your code currently cannot work. You are attempting to do this:

  • auto && __range = myDateRange; - Ok, our range is a DateRange. This is fine.

  • auto __begin = __range.begin();
    auto __end = __range.end();
    So __begin and __end are now Time_type*... This is already looking bad.

  • Now the loop will increment __begin, but it is a TimeType* that does not point into an array. Dereferencing the incremented __begin (as done in the next statement) will thus be Undefined Behavior. Note how operator++ of the range_expression is never called.

Regardless of if you fix the compiler error (and missing initialization in DateRange(Time_type_t end)), this approach will not work. You need an iterator class that keeps a reference to your DateRange. This iterator is returned by begin() and end() and has itself an operator++() and operator*() (which would return an appropriate time_point).

2
votes

The value you return from begin needs to eventually be equal to the value you return from end by some amount of ++. Currently whether this is possible is unspecified, as you return pointers to distinct objects.

You probably need a separate iterator type.

template<typename Duration, typename Clock = high_resolution_clock>
class DateRange {
   using Time_type = time_point<Clock, Duration>;

   class iterator {
     iterator & operator++(){
       present += Duration(1);
       return *this;
     }

     iterator operator++(int){
       iterator res = *this;
       ++res;
       return res;
     }

     Time_type * operator->(){
       return &present;
     }

     Time_type operator*(){
       return present;
     }

     Time_type present;
   };

   Time_type m_begin;
   Time_type m_end;
public:
  DateRange(Time_type_t start, Time_type_t end) :
    m_begin(start),
    m_end(end)
    {}
  DateRange(Time_type_t end) :
    m_begin(Clock::now()),
    m_end(end)
    {}
  iterator begin(){
    return m_begin;
  }
  iterator end() {
    return m_end;
  }
};