web-dev-qa-db-ja.com

イテレータのデクリメントはどの程度移植可能ですか?

会社のソースコードでend()イテレータのデクリメントが発生しましたが、奇妙に見えます。私が覚えている限り、これは一部のプラットフォームでは機能しましたが、他のプラットフォームでは機能しませんでした。多分私は間違っているかもしれませんが、それについて標準で役立つものを見つけることができませんでした。標準は、end()が過去の値である反復子を返すとだけ言っていますが、デクリメント可能であることが保証されていますか?そのようなコードはどのように標準に一致しますか?

std::list<int>::iterator it = --l.end();

前もって感謝します。

52
ledokol

これは関連する条項だと思います:

ISO/IEC 14882:2003 C++ Standard 23.1.1/12 – Sequences

表68は、一部のタイプの順次コンテナーに提供されているシーケンス操作をリストしています。実装は、「コンテナー」列に示されているすべてのコンテナータイプに対してこれらの操作を提供し、償却された一定の時間がかかるようにそれらを実装するものとします。

 + -------------------------------------------- -------------------------------- + 
 |表68 | 
 + -------------- + ----------------- + -------- ------------- + --------------------- + 
 |式|戻り型|運用|コンテナ| 
 | | |セマンティクス| | 
 + -------------- + ----------------- + ---------- ----------- + --------------------- + 
 | a.front()|参照; | * a.begin()|ベクトル、リスト、デック| 
 | | const_reference | | | 
 | |定数a | | | 
 + -------------- + ----------------- + ---------- ----------- + --------------------- + 
 | a.back()|参照; | *-a.end()|ベクトル、リスト、デック| 
 | | const_reference | | | 
 | |定数a | | | 
 ............................................ .................................. 
 。 。 。 。
 。 。 。 。
 ............................................ .................................. 
 | a.pop_back()| void | a.erase(-a.end())|ベクトル、リスト、デック| 
 ....................................... ................................................. 
 。 。 。 。
 。 。 。 。

したがって、リストされているコンテナの場合、end()から返されたイテレータがデクリメント可能であるだけでなく、デクリメントされたイテレータも逆参照可能である必要があります。 (もちろん、コンテナーが空でない限り、これは未定義の動作を引き起こします。)

実際、Visual C++コンパイラに付属するvectorlistおよびdequeの実装は、表とまったく同じように実行します。もちろん、それはすべてのコンパイラが次のようにすることを意味するものではありません。

_// From VC++'s <list> implementation

reference back()
    {    // return last element of mutable sequence
    return (*(--end()));
    }

const_reference back() const
    {    // return last element of nonmutable sequence
    return (*(--end()));
    }
_

表のコードについて注意してください。

ISO/IEC 14882:2003 C++ Standard 17.3.1.2/6 –要件

場合によっては、セマンティック要件がC++コードとして提示されます。 このようなコードは、構成の別の構成への等価性の仕様として意図されており、必ずしも構成の実装方法としてではありません。

したがって、実装がbegin()およびend()に関してこれらの式を実装しない可能性があることは事実ですが、C++標準では、2つの式が同等であると規定しています。言い換えると、a.back()*--a.end()は、上記の句によると同等の構成です。これは、a.back()のすべてのインスタンスを*--a.end()に置き換えることができ、その逆も可能で、コードを引き続き機能させる必要があることを意味しているようです。


Bo Perssonによると、私が手元にあるC++標準の改訂 欠陥があります 表68に関して。

提案された解像度:

23.1.1/12の表68「オプションのシーケンス操作」の「a.back()」の仕様を

_*--a.end()
_

_{ iterator tmp = a.end(); --tmp; return *tmp; }
_

および「a.pop_back()」の仕様

_a.erase(--a.end())
_

_{ iterator tmp = a.end(); --tmp; a.erase(tmp); }
_

一時的なものでない限り、end()から返されたイテレータをデクリメントし、デクリメントしたイテレータを逆参照することができます。

50
In silico

_std::prev_のドキュメントから

式--c.end()はしばしばコンパイルされますが、コンパイルが保証されるわけではありません。c.end()は右辺値式であり、右辺値のデクリメントが機能することが保証されていることを指定する反復子の要件はありません。特に、イテレーターがポインターとして実装されている場合、-c.end()はコンパイルされませんが、std :: prev(c.end())はコンパイルされます。

つまり、接頭辞デクリメント操作の実装は inside class form iterator iterator::operator--(int)ではないかもしれませんが、クラスフォームiterator operator--(iterator&, int)の外ではオーバーロードされます。

したがって、_std::prev_を優先するか、以下を実行する必要があります:{ auto end = a.end(); --end; };

2
Izana

このコードは問題です。リストが空の場合は問題があります。

それを確認してください。リストが空でない場合、コードは非常に良好です。

0