カスタム イテレータを使用してカスタム コンテナーを作成しました。コンテナーの特定の機能により、反復子は遅延評価する必要があります。質問のために、コードの関連部分は、このように実装された反復子の逆参照演算子です
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;
}
}
イテレータ自体がテンプレートであるため、その関数はコンパイラによって自由にインライン化できます。
最適化を行わずにコードをコンパイルすると、期待どおりに戻り値が更新されます。リリース コンパイラの最適化 (GCC 4.9 では -O2) を使用すると、m_Current メンバーが変更可能としてマークされていても、最適化済みとしてマークした行がコンパイラによって最適化されることがあります。結果として、戻り値は反復子が指す値と一致しません。
これは予期される動作ですか? const とマークされていても、その関数の内容を評価する必要があることを指定する移植可能な方法を知っていますか?
質問が役立つほど網羅的であることを願っています。この場合、詳細が役立つかどうかアドバイスをお願いします。
編集:
1 つのコメントに答えるために、これは小さなテスト プログラムから取られた潜在的な使用法です。
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;
テスト結果は、最後の 2 行を除くすべてのテストで期待どおりです
最適化をオンにすると、テストの最後の 2 行が壊れます。
このシステムは実際にうまく機能し、他のイテレーターほど危険ではありません。もちろん、コンテナーが彼の鼻の下で削除された場合は失敗します。参照を保持するだけでなく、コピーによって返された値を使用する方がおそらく安全ですが、これはトピックから外れています