Bérenger
Bérenger

Reputation: 2748

Convert the end() iterator to a pointer

To get to the point: is the following safe ?

vector<int> v;
int const* last = &*v.end(); 
// last is never dereferenced

My concern is that the trick to get a plain old pointer from an iterator forces to dereference the end() iterator, which is not valid... even if it is just to take the pointer back!

Backgroud: I am trying to create a collection of entries indexed by arbitrary types (especially integers and pointer to objects).

template<class IT>
/// requires IT implements addition (e.g. int, random-access iterator)
class IndexingFamily {
    public:
        using IndexType = IT;

        IndexingFamily(IndexType first, IndexType last);
        int size() const;
        IndexType operator[](int i) const;
    private:
        IndexType first;
        IndexType last;
};

template<class IT> IndexingFamily<IT>::
IndexingFamily(IndexType first, IndexType last) 
    : first(first)
    , last(last) {}

template<class IT> auto IndexingFamily<IT>::
size() const -> int {
    return last-first;
}

template<class IT> auto IndexingFamily<IT>::
operator[](int i) const -> IndexType {
    return first+i;
}

template<class IT, class ET>
struct IndexedEntry {
    using IndexType = IT;
    using EntryType = ET;

    IndexType index;
    EntryType entry;
};

template<class IT, class ET>
class CollectionOfEntries {
    public:
        using IndexType = IT;
        using EntryType = ET;

        /// useful methods
    private:
        IndexingFamilyType indexingFamily;
        vector<EntryType> entries;
};


struct MyArbitraryType {};


int main() {
    MyArbitraryType obj0, obj1, obj2;
    vector<MyArbitraryType> v = {obj0,obj1,obj2};

    using IndexType = MyArbitraryType const*;
    IndexingFamily<IndexType> indexingFamily(&*v.begin(),&*v.end());

    using EntryType = double;
    using IndexedEntryType = IndexedEntry<IndexType,EntryType>;
    IndexedEntry entry0 = {&obj0,42.};
    IndexedEntry entry1 = {&obj1,43.};
    vector<IndexedEntryType> entries = {entry0,entry1};

    CollectionOfEntries coll = {indexingFamily,entries};

    return 0;
}

Upvotes: 5

Views: 1429

Answers (2)

Vlad from Moscow
Vlad from Moscow

Reputation: 310950

The iterator returned by member function end() "points" after the last element of a vector. You may not dereference it. Otherwise the program will have undefined behavior.

You can get the corresponding pointer the following way

vector<int> v;
int const *last = v.data() + v.size(); 

If you want to use the iterator returned by the member function end() you can write

vector<int> v;
int const *last = v.data() + std::distance( v.begin(), v.end() ); 

Take into account that member function data() returns a raw pointer of the memory extent occupied by vector's data.

Upvotes: 1

Peter
Peter

Reputation: 36597

Dereferencing an end() iterator gives undefined behaviour, with any standard container.

For a vector you can get a pointer corresponding to the end() iterator using

pointer_to_end = v.empty() ? 0 : (&(*v.begin()) + v.size());

or

pointer_to_end = v.data() + v.size();   // v.data() gives null is size is zero

The check of v.empty() is needed since, if v is empty, v.begin() == v.end(). For C++11 or later, use of nullptr instead of 0 in the above is often considered preferable.

Upvotes: 3

Related Questions