3

対応する非 constのような関数をconst_iterator提供しないのはなぜですか?const_iterator::base()iteratorreverse_iterator

次の擬似コードを検討してください(たとえば、幾何学的アルゴリズム):

std::container< point > universe;
auto it = std::cbegin(universe);
std::list< decltype(it) > interesting_subset = sieve(it, std::cend(universe));
auto structure = algorithm(interesting_subset);

universeすべての入力ポイントはどこにありますか。のメンバーのサブセットに含む反復子をsieve()-ing した後。以下は、 のメンバーへの参照 (反復子) で構成される から生成される を構築します。interesting_subsetuniversealgorithm()structureinteresting_subsetuniverse

最後に、point結果に含まれる sを変更しstructureます (たとえば、シフトします)。しかし、同様に、アクション中の modyfining からそれらを保護したいので、 /の反対として/algorithmを使用しました。最後に、 source への参照しかありません。std::cbeginstd::cendstd::beginstd::endconst_iteratorpoint

これは、iterator std::container< T >::const_iterator::base() constSTL コンテナーに含めたいメンバー関数の使用例です。

4

4 に答える 4

3

const_iterator が const_iterator::base() 関数を提供しないのはなぜですか?

const-safe を維持するため。このような機能を提供することは、ここで詳しく説明したように非常に危険です。

最後に、結果の構造に含まれるポイントを変更します (たとえば、それらをシフトします)。しかし、同様に、アルゴリズムの動作中の modyfining からそれらを保護したいので、std::begin/std::end の反対として std::cbegin/std::cend を使用しました。最後に、ソース ポイントへの const_iterator 参照しかありません。

さて、あなたはbase-member で間違ったことを求めています。確かにそれはあなたの問題を解決するでしょうが、言ったように、それはあまりにも危険です. もう一度質問をさせてください:

const_iteratorオブジェクトへのアクセスとコンテナーへの非定数アクセスがある場合、iterator参照されたオブジェクトへのアクセスを効率的に (一定の時間で) 取得するにはどうすればよいですか?

これを正確に行うための凝ったトリックを次に示します。

template <typename Container, typename ConstIterator>
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
    return c.erase(it, it);
}

私はトリックの功績を認めません。https://stackoverflow.com/a/10669041/2079303から見つけました。Howard Hinnant と Jon Kalb の功績によるものです。

その回答のコメントで説明されているように、このトリックはすべての標準コンテナーで機能しますが、提供する必要がないため、すべての可能な標準準拠のサードパーティ コンテナーで機能するとは限りませんerase

const_iterator個人的には、標準のコンテナーには、与えられたa を に変換する非 const メンバー関数があることを望みiteratorますが、そうではないため、回避する必要があります。

于 2015-10-29T13:12:39.793 に答える
1

興味深い質問です。ポイントについて推論している間はポイントを保護できるようにする必要がありますが、それらについて推論した後は、変更可能な参照をそれらに返します。

ポイントについて推論した結果、実際に返されるのはそれらの「名前」または「識別子」です。

sieve()それらを名前でマッピングし、関連する名前のベクトルを返すことができると考えられます。正式な名前 (一意の番号、テキスト文字列など) を格納するオーバーヘッドを避けたい場合は、そのような名前を単にアドレスにすることができます。

const オブジェクトのアドレスを名前として使用する場合、もちろん、それを変更可能なオブジェクトへの参照に戻すには、const_cast. これは、const キャストを合理的に使用できる数少ないケースの 1 つと見なされるかもしれません。そうするときは、フォールアウトを制限するためにユーティリティ クラスにカプセル化する必要があります。

編集:解決策を再考しました。これは、手に負えないクライアント コードによって悪用できなくなりました。

#include <iostream>
#include <vector>

struct point { int x, y; };

inline std::ostream& operator<<(std::ostream& os, const point& p)
{
    os << "(" << p.x << ", " << p.y << " )";
    return os;
}

struct point_collection
{
    using container_type = std::vector<point>;

    point_collection(container_type points) : _points(std::move(points)) {}

    using const_iterator = const point*;
    using iterator = point*;

    const_iterator begin() const { return &*_points.begin(); }
    const_iterator end() const { return &*_points.end(); }

    // note that this function is only available on a non-const point_collection
    point& mutable_reference(const_iterator input)
    {
        // could put an assert in here to ensure that input is within our range
        return *const_cast<iterator>(input);
    }

    container_type _points;
};


std::vector<point_collection::const_iterator> sieve(point_collection::const_iterator first,
                                                    point_collection::const_iterator last)
{
    std::vector<point_collection::const_iterator> result;

    for ( ; first != last ; ++first )
    {
        if (first->x > 6)
            result.push_back(first);
    }
    return result;
}


point_collection make_a_universe()
{
    return {
        std::vector<point> {
            { 10, 10 },
            { 6, 6 }
        }
    };
}

auto main() -> int
{
    using namespace std;
    auto universe = make_a_universe();

    auto interesting = sieve(universe.begin(), universe.end());

    for (auto point_id : interesting) {
        auto& p = universe.mutable_reference(point_id);
        p.x = -p.x;
        cout << p << endl;
    }
    return 0;
}

期待される出力:

(-10, 10 )
于 2015-10-29T11:07:26.927 に答える
0

Areverse_iteratorは、基になるオブジェクトが const であるかどうかにかかわらず変更されず、const_iterator はイテレーターに関連していない (変換可能であるという要件と参照を除く) ため、リンゴとオレンジを比較しています。

もしconst_iterator本当にそれが非定数イテレータへのアクセスを提供するなら、それは次baseのようなことをすることが可能になるでしょう

auto const & a = std::vector<int>(20, 1);
auto it = std::cbegin(a);
*it.base() = 4;

標準ではこれを許可しています。

あなたは - 原則として - によって提供される変更に対する保護を回避したいと考えていconst_iteratorます。しかし、それconst_iteratorが問題です。ですから、それは良い考えでも、起こりそうもないことだと私は思います (そして願っています)。

Eventhough I think this is a XY Problem, I answered to the question rather than providing guidance on how to do it properly.

-edit-

If you want a const_iterator to be able to return an iterator, it is iterator that you want.

Your intention seems to be

  1. Pass const_iterator to sieve where sieve is not allowed to change the elements.
  2. Pass the very same iterators to algorithm allowing it to modify them.

You require two different things from one and the same object. Nobody keeps the implementor of sieve from using it.base() so there would be no guarantee at all that sieve does not change the elements. This is, I repeat, the point of the matter const_iterator.

If there was any way of changing things using const_iterators it would just break them.

于 2015-10-29T11:10:39.563 に答える