Compiler optimization breaks a lazy iterator

I wrote a custom container with my custom iterator. Due to the nature of the container, the iterator should be evaluated lazily. For the sake of the matter, the corresponding part of the code is an iterator dereference operator, which is implemented in this way.

template<typename T>
struct Container
{
  vector<T> m_Inner;

  // This should calculate the appropriate value.
  // In this example is taken from a vec but in 
  //the real use-case is calculated on request
  T Value(int N)
  { m_Inner.at(N); }
}

template<typename T>
struct Lazy_Iterator
{
  mutable pair<int, T> m_Current;
  int Index
  Container<T>* C

  Lazy_Iterator(const Container& Cont, int N):
    m_Current{Index, T{}}, Index{N}, C{&Cont}
  {      }

  pair<int, T>&
  operator*() const // __attribute__((noinline)) (this cures the symptom)
  {
      m_Current.first = Index; /// Optimized out
      m_Current.second = C->Value(Index); /// Optimized out
      return m_Current;
  }

}

Since the iterator itself is a template, its functions can be freely embedded by the compiler.

When I compile the code without optimization, the return value is updated as expected. When I use release compiler optimization (-O2 in GCC 4.9), in some cases the compiler optimizes the lines that I mark as optimized, although the m_Current element is marked as mutable. As a result, the return value does not match the value that the iterator should refer to.

? - , , const?

, , . , , .

Edit:

, , :

Container<double> myC;
Lazy_Iterator<double> It{myC, 0}
cout << "Creation: " << it->first << " , " << it->second << endl;

auto it2 = it;
cout << "Copy: "<<  it2->first << " , " << it2->second << endl;

cout << "Pre-increment: " << (it++)->first << " , " << it->second << endl;
cout << "Post-increment: " << (++it)->first << " , " << it->second << endl;
cout << "Pre-decrement: " << (it--)->first << " , " << it->second << endl;
cout << "Post-decrement: " << (--it)->first << " , " << it->second << endl;
cout << "Iterator addition: " << (it+2)->first << " , " << (it+2)->second << endl;
cout << "Iterator subtraction: "<< (it-2)->first << " , " << (it-2)->second << endl;

reverse_iterator<Lazy_Iterator> rit{it};
cout << "Reverse Iterator: " << rit->first << " , " << rit->second << endl;

auto rit2 = rit;
cout << "Reverse Iterator copy: " << rit2->first << " , " << rit2->second << endl;

cout << "Rev Pre-increment: " << (rit++)->first << " , " << rit->second << endl;
cout << "Rev Post-increment: " << (++rit)->first << " , " << rit->second << endl;
cout << "Rev Pre-decrement: " << (rit--)->first << " , " << rit->second << endl;
cout << "Rev Post-decrement: " << (--rit)->first << " , " << rit->second << endl;
cout << "Rev Iterator addition: " << (rit+2)->first << " , " << (rit+2)->second << endl;
cout << "Rev Iterator subtraction: "<< (rit-2)->first << " , " << (rit-2)->second << endl;

,

, .

, . , , , , , , ,

+4
4

, reverse_iterator ( .base()) , : . reverse_iterator return *(--internal_iterator); , - .

, , , .

, GCC 4.9 . . , , GCC.

:

24.5.1.3.4 * [reverse.iter.op.star]

reference operator*() const;

1 :

deref_tmp = current;  
--deref_tmp; 
return *deref_tmp;

2 [. -, , , . (. 24.2.) -end note]

: 198.

, .

: P0031 ++ 17 Working Draft. , reverse_iterator , , .

+2

", m_Current "

, mutable. . const mutable .

, ? , , , m_Current , , m_Current . no-op:

Lazy_Iterator LI = foo(); // Theoretically writes
*LI = bar(); // Overwrites the previous value.
+2

, ( GCC 4.9) , undefined, O2 (O2 , undefined ).

Container<T> 

.

, std-, , . , ;) ( ).

+1

Revolver_Ocelot , _. :

24.5.1.3.4 * [reverse.iter.op.star]

reference operator*() const;

1 :

deref_tmp = current;  
--deref_tmp;  
return *deref_tmp;

2 [: -, , , . (. 24.2.) -end note]

stl_iterator.c , GCC 4.9 Debian 8:

  /**
   *  @return  A reference to the value at @c --current
   *
   *  This requires that @c --current is dereferenceable.
   *
   *  @warning This implementation requires that for an iterator of the
   *           underlying iterator type, @c x, a reference obtained by
   *           @c *x remains valid after @c x has been modified or
   *           destroyed. This is a bug: http://gcc.gnu.org/PR51823
  */
  reference
  operator*() const
  {
_Iterator __tmp = current;
return *--__tmp;
  }

:

: , , @c x, , @c * x @c x . : http://gcc.gnu.org/PR51823

0

All Articles