14

Is there any way to convert a vector::iterator into a pointer without having access to the vector itself? This works only if the vector is available:

typedef vector<int> V;
int* to_pointer1( V& v, V::iterator t )
{
    return v.data() + (t-v.begin() );
}

But how can this be done without having the vector around? This wouldn't work:

int* to_pointer2( V::iterator t )
{
    // the obvious approach doesn't work for the end iterator
    return &*t;
}

In fact I would have expected this to be a very basic and popular question but for some reason I couldn't find it.

pentadecagon
  • 4,717
  • 2
  • 18
  • 26
  • 3
    I don't think it should work for the end iterator - it represents just past the last element and dereferencing this pointer is undefined behaviour. – Nick Jun 18 '14 at 08:41
  • @Nick I want it to work for any valid iterator, including the end. It should be possible, because `to_pointer1` also covers this case. – pentadecagon Jun 18 '14 at 08:43

4 Answers4

13

In general you are not guaranteed that there is a pointer corresponding to an iterator that refers to an ordinary vector item.

In particular std::vector<bool> is specialized so that &*it won't even compile.

However, for other vectors it's only1 the formally-pedantic that stops you from doing &*it also for the end iterator. In C99 it is, as I recall, formally valid to do &*p when p is an end pointer, and a goal of std::vector is to support all ordinary raw array notation. If I needed this I would just ignore the formal-pedantic, and write code with a view towards some future revision of the standard that would remove that imagined obstacle.

So, in practice, just do &*it. :)

#include <iostream>
#include <vector>
using namespace std;

auto main() -> int
{
    vector<int> x( 5 );
    cout << &*x.begin() << " " << &*x.end() << endl;
    cout << &*x.end() - &*x.begin() << endl;        // Works nicely IN PRACTICE.
}

But do remember that you can't do this for vector<bool>.


1) In a comment elsewhere user pentadecagon remarks: “Try -D_GLIBCXX_DEBUG, as documented here: gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode_using.html.” g++ often provides some means of bringing any formal UB to the front, e.g. “optimization” of formally UB loops. A solution in this particular case is simply to not ask it to do this, but more generally one may have to explicitly ask it to not do it.

Community
  • 1
  • 1
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
7

No, this is not currently possible. n3884 Contiguous Iterators: A Refinement of Random Access Iterators proposes a free function std::pointer_from which would satisfy your requirement:

Expression: std::pointer_from(a)
Return type: reference
Operational semantics: if a is dereferenceable, std::address_of(*a); otherwise, if a is reachable from a dereferenceable iterator b, std::pointer_from(b) + (a – b); otherwise a valid pointer value ([basic.compound]).

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

What you are asking is not possible for the end iterator; By definition, the end iterator points to a hypothetical element past the end of the array (i.e. de-referencing it, is always UB).

You said:

@Nick I want it to work for any valid iterator, including the end. It should be possible, because to_pointer1 also covers this case.

to_pointer1 doesn't cover this case. to_pointer1 returns an invalid memory address (Actually the address is probably valid, but there is no data there).

utnapistim
  • 26,809
  • 3
  • 46
  • 82
  • What exactly is the problem with `to_pointer1` then? As you say the address is valid. – pentadecagon Jun 18 '14 at 09:07
  • @pentadecagon, If called with the end iterator, the address returned, is "one-past" the last element of the array. This may be an address allocated for something else in your application, an address within the block allocated for the vector (if the vector has allocated more elements than it's size) or an address within unused memory. Theoretically, it could also be an address allocated within memory allocated for another application (though in practice this probably won't happen). Also, if the vector is empty, it is possible the result of `vector.data() + xyz;` is `nullptr + xyz;`. – utnapistim Jun 18 '14 at 09:19
  • 1
    Sure. But I don't see how this is a problem. It's still a valid pointer, I can do pointer arithmetic with it, it's at least as useful as the end-iterator but a bit more versatile because I can pass it to C-functions. – pentadecagon Jun 18 '14 at 09:25
  • It is _not_ a valid pointer, it is a pointer to memory that may be inaccessible, unused, part of the block of memory in the vector, or pointing to something else. While it allows you to perform the same operations, it does so _unsafely_. At the calling site, you may remember that it is unsafe to use the pointer, but if you pass it to some other function (like you suggest) that function has no way of determining if the pointer is valid (unless you pass that information as well). You are looking for trouble down the road with this implementation. – utnapistim Jun 18 '14 at 09:44
  • @utnapistim the intent of `vector` is that it be possible to treat `data()` as the beginning of an array of size `size()`, which means that `data() + size()` is a valid but nondereferenceable pointer. 23.3.6.4p2 says that "*`[data(),data() + size())` is a valid range*". – ecatmur Jun 18 '14 at 09:49
  • the end pointer is valid but with the current standard the corresponding reference isn't. – Cheers and hth. - Alf Jun 18 '14 at 09:52
0

I use this template:

template<typename It>
inline auto ptr(It const&it) -> decltype(std::addressof(*it))
{ return std::addressof(*it); }

In practice, this will work for most iterators. Exceptions are objects where either *it is not defined (vector<bool>::iterator) or where it points to nowhere (rather than to an past-the-last element). For your particular purpose, it shall be okay, except for vector<bool> (when the concept of a pointer is not sensible).

Walter
  • 44,150
  • 20
  • 113
  • 196
  • 1
    `*it` isn't a valid expression for the end iterator, is it? – pentadecagon Jun 18 '14 at 08:48
  • GCC in debug mode doesn't like it and bails out at runtime for `it=vec.end()`. – pentadecagon Jun 18 '14 at 08:53
  • clang is happy with this, even with `-Weverything`. I don't think that `addressof(*(vector<>::end()))` is invalid, though for certain iterators (whence `end()` *points* to null) it will. – Walter Jun 18 '14 at 08:56
  • @pentadecagon What do you mean with *GCC in debug mode*? I tried `g++ -std=c++11 -g -Wall -Wextra -pedantic` which compiles (without warnings) and ran without error giving the expected address even for the end iterator. – Walter Jun 18 '14 at 09:06
  • 1
    Try `-D_GLIBCXX_DEBUG`, as documented here: https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode_using.html – pentadecagon Jun 18 '14 at 09:10