6

たとえば、std::vector<int>::iterator it = --(myVec.end());. これは GCC 4.4 で動作しますが、移植性がないという噂を耳にしました。

4

3 に答える 3

8

メンバー関数std::vector<int>::iteratorを持つオブジェクト型である場合にのみ機能します。operator++スカラー型 ( などint *) の場合、またはoperator++非メンバー関数の場合は失敗します。

5.3.2 インクリメントとデクリメント [expr.pre.incr]

1 - プレフィックスのオペランドは、 [...]++を追加して変更されます。オペランドは変更可能な左辺値でなければなりません。[...] 2 - [...] prefix [...] のオペランドの要件は[...] prefix の要件と同じです。[...]1
--++

非 const 非静的メンバー関数は一時オブジェクトで呼び出すことができます ( const9.3.2p3 に従って非オブジェクト型であるため) が、非メンバー関数の左辺値参照パラメーターは一時オブジェクトにバインドできません (13.3.3.1. 4p3)。

struct S { S &operator++(); };
struct T { }; T &operator++(T &);
typedef int U;

++S();  // OK
++T();  // fails
++U();  // fails

これは、コンパイラとは関係なく、標準ライブラリとは関係がないことを意味します。libstdc++ がstd::vector<int>::iteratormember のオブジェクト型で実装されていることを確認したように、コードは同じコンパイラとisoperator++の別の標準ライブラリで簡単にコンパイルできます。その場合、失敗します。std::vector<int>::iteratorint *

std::vectorstd::arrayおよびstd::stringは、スカラー (ポインター) イテレーターを使用して適切に実装できる唯一のコンテナー テンプレートですが、++他のコンテナーのイテレーターの呼び出しが安全であるとは限りません。上記のように非メンバーoperator++を持つことができますT

終了前の要素への反復子を作成するには、次を使用しますstd::prev

std::vector<int>::iterator it = std::prev(myVec.end());

std::prevおよびstd::nextC++11 の新機能ですが、C++03 では簡単に実装できます。

于 2012-11-30T18:59:56.670 に答える
6

いいえ、一般的には機能しません。

C++11 には次auto it = std::prev(myVec.end());のものがあります。これは確実に動作します。

C++03 を使用している場合、Boost には同様の機能がありますが、完全に記述するのは簡単です。

template <typename BidirectionalIterator>
BidirectionalIterator
    prev(BidirectionalIterator x,
         typename std::iterator_traits<BidirectionalIterator>::difference_type n = 1)
{
    std::advance(x, -n);
    return x;
}

これが意味を持つためには、範囲内に少なくとも 1 つの要素が必要であることに注意してください。


メソッドが一般的に機能しない例を次に示します。これを単純化したものと考えてstd::vector<>ください。

#include <iterator>

namespace std_exposition
{
    template <typename T>
    struct vector
    {
        // this is compliant:
        typedef T* iterator;

        iterator end()
        {
            return std::end(data);
        }

        T data[4];
    };

    // manually implemented std::prev:
    template <typename BidirectionalIterator>
    BidirectionalIterator
        prev(BidirectionalIterator x,
             typename std::iterator_traits<BidirectionalIterator>::difference_type n = 1)
    {
        std::advance(x, -n);
        return x;
    }
}

テストプログラム:

int main()
{
    std_exposition::vector<int> myVec;

    // Won't compile (method in question):
    auto it0 = --(myVec.end());

    // Compiles
    auto it1 = std::prev(myVec.end());
    auto it2 = std_exposition::prev(myVec.end());
}

対応するstd::nextものもあり、ここに実装されています。

template <typename BidirectionalIterator>
BidirectionalIterator
    next(BidirectionalIterator x,
         typename std::iterator_traits<BidirectionalIterator>::difference_type n = 1)
{
    std::advance(x, n);
    return x;
}
于 2012-11-30T19:03:31.883 に答える
3

myVec.end()メンバー関数によってオーバーロードされた演算子を持つクラス型のオブジェクトを返すかどうかを知る方法がないため、これは確かに移植性がありませ--ん (おそらく通常の生のポンターでさえ)。前者の場合、オーバーロード--されたものはコンパイルされます (メンバー関数によってオーバーロードされた演算子は右辺値に適用できます) が、後者の場合はそうではありません。

于 2012-11-30T19:12:13.957 に答える