1

If I have the end iterator to a container, but I want to get a raw pointer to that is there a way to accomplish this?

Say I have a container: foo. I cannot for example do this: &*foo.end() because it yields the runtime error:

Vector iterator not dereferencable

I can do this but I was hoping for a cleaner way to get there: &*foo.begin() + foo.size().

EDIT:

This is not a question about how to convert an iterator to a pointer in general (obviously that's in the question), but how to specifically convert the end iterator to a pointer. The answers in the "duplicate" question actually suggest dereferencing the iterator. The end iterator cannot be dereferenced without seg-faulting.

Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • 3
    `&*foo.end()` doesn't make sense anyway, since `end()` is an iterator to an element *after the last* element of the container, trying to access it invokes UB. – SingerOfTheFall Aug 18 '15 at 13:25
  • 2
    Are you really asking about containers in general, or specifically `vector`? As LRiO says, it doesn't even make sense in general. The end iterator doesn't necessarily "point" to anything, so what would a pointer to that location even mean? For `forward_list` the end iterator is typically a null pointer, so is the same for all lists, for `std::list` it points to a special sentinel, not an element of the `value_type`. – Jonathan Wakely Aug 18 '15 at 13:28
  • @JonathanWakely I'm trying to accomplish something like this: http://stackoverflow.com/a/32073224/2642059 so I need to get a `char*` from `end`. – Jonathan Mee Aug 18 '15 at 13:33
  • @SingerOfTheFall I'm trying to accomplish something like this: http://stackoverflow.com/a/32073224/2642059 so I need to get a `char*` from `end`. – Jonathan Mee Aug 18 '15 at 13:33
  • 1
    Then you should be adding the size to the address of the first element, I don't know why you think that isn't clean. If you have a container that stores its data in a contiguous array then pointer arithmetic is exactly the right approach. – Jonathan Wakely Aug 18 '15 at 13:38
  • @JonathanWakely Yeah it is contiguous. So I guess that's my ticket. Visual Studio implementation provides the public member `_Ptr` so I am able to do something like this: `&*foo.end()._Ptr` but I wanted something cross platform... – Jonathan Mee Aug 18 '15 at 13:50
  • That might break in a future release of Visual Studio and definitely won't work on any other platform, and is undefined behaviour when `v.size()==v.capacity()` because you dereference a pointer that doesn't point to allocated memory, so is hideously unclean, whereas some simple pointer arithmetic is safe and correct. – Jonathan Wakely Aug 18 '15 at 18:47
  • @JonathanWakely You misunderstand. I'm asking this question because I *don't* want to do that. You'll also notice that I don't pronounce it "clean". I'm simply pointing out that there *shouldn't* be a need to do pointer arithmetic. The implementation is already holding the exact value that I want. – Jonathan Mee Aug 18 '15 at 18:51
  • Ah OK, I did misunderstand "so I am able to do something like this" as meaning that was your preferred solution. That aside, how does the implementation get that exact value that you want? Pointer arithmetic! The vector gets a pointer back from its allocator, and it obtains the value in the end iterator by adding the number of initialized elements to it. I don't understand why you think there's something wrong with that approach. – Jonathan Wakely Aug 18 '15 at 21:24
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/87317/discussion-between-jonathan-mee-and-jonathan-wakely). – Jonathan Mee Aug 18 '15 at 21:51

2 Answers2

5

The correct way to access the end of storage is:

v.data() + v.size()

This is because *v.begin() is invalid when v is empty.

The member function data is provided for all contiguous containers (vector, string and array).

From C++17 you will also be able to use the non-member functions:

data(v) + size(v)

This works on raw arrays as well.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
4

In general? No.

And the fact that you're asking indicates that something is wrong with your overall design.


For vectors, arrays, strings? Sure… but why?

Just get a pointer to a valid element, and advance it:

std::vector<T> foo;
const T* ptr = foo.data() + foo.size();

As long as you don't dereference such a pointer (which is almost equivalent to dereferencing the iterator, as you did in your attempt) it is valid to obtain and hold such a pointer, because it points to the special one-past-the-end location.

Note that &foo[0] + foo.size() has undefined behaviour if the vector is empty, because &foo[0] is &*(foo.data() + 0) is &*foo.data(), and (just like in your attempt) *foo.data() is disallowed if there's nothing there. So we avoid all dereferencing and simply advance foo.data() itself.

Anyway, this only works for the case of vectors1, arrays and strings, though. Other containers do not guarantee (or can be reasonably expected to provide) storage contiguity; their end pointers could be almost anything, e.g. a "sentinel" null pointer, which is unlikely to be of any use to you.

That is why the iterator abstraction is there in the first place. Stick to it if you can, instead of delving into raw pointer usage.


1. Excepting std::vector<bool>.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 3
    _"This only works for the case of vectors"_ ... and `std::array` and `std::basic_string` (the three types of _contiguous container_ defined in recent drafts). – Jonathan Wakely Aug 18 '15 at 13:32
  • @JonathanWakely: Ooh, strings are deemed "containers" finally? Nice. – Lightness Races in Orbit Aug 18 '15 at 13:33
  • If you squint just right yes, :) Clause 21 says `basic_string` is a contiguous container now. – Jonathan Wakely Aug 18 '15 at 13:40
  • @JonathanWakely: Well it's about time :P – Lightness Races in Orbit Aug 18 '15 at 13:40
  • @LightnessRacesinOrbit The "why" for this may have many good answers. One good answer is [this](http://stackoverflow.com/a/31941235/2642059) :) – Jonathan Mee Aug 19 '15 at 11:34
  • @JonathanMee: You mean the one which I pointed out (and you conceded) was an invalid answer to the stated question? lol – Lightness Races in Orbit Aug 19 '15 at 11:38
  • @LightnessRacesinOrbit I thought you'd remember! Isn't it great? This answer helps us accomplish a solution that does work there! – Jonathan Mee Aug 19 '15 at 11:50
  • Could this code cause undefined behaviour for a [zero-sized array](http://stackoverflow.com/questions/24152435/is-there-a-reason-for-zero-sized-stdarray-in-c11)? In N3936, `array::data()` returns an *unspecified* pointer. Adding `0` to a null or invalid pointer causes UB (expr.add/5). `vector` possibly does not suffer the same issue because it is specified that [data(), data()+size()) must be a valid range even if the vector is empty. (But it's not clear whether `data()+size()` is evaluated in that range specification...) – M.M Aug 21 '15 at 00:39