ChatGPT解决这个技术问题 Extra ChatGPT

Convert iterator to pointer?

I have a std::vector with n elements. Now I need to pass a pointer to a vector that has the last n-1 elements to a function.

For example, my vector<int> foo contains (5,2,6,87,251). A function takes vector<int>* and I want to pass it a pointer to (2,6,87,251).

Can I just (safely) take the iterator ++foo.begin(), convert it to a pointer and pass that to the function? Or use &foo[1]?

UPDATE: People suggest that I change my function to take an iterator rather than a pointer. That seems not possible in my situation, since the function I mentioned is the find function of unordered_set<std::vector*>. So in that case, is copying the n-1 elements from foo into a new vector and calling find with a pointer to that the only option? Very inefficient! It's like Shlemiel the painter, especially since i have to query many subsets: the last n-1, then n-2, etc. elements and see if they are in the unordered_set.

Could you explain a little bit more what you're really trying to do? I'm a little confused - it seems like you're trying to do something with unordered_set<> and unordered_set<>::find() that they aren't really designed to do (but I'm quite possibly just not understanding).
Why are you using a vector to represent an unordered set in the first place? If you're doing multiple membership queries, there are much more efficient implementations. You may want to create a set and then query against it.
I have a set S of known sequences. For each new sequence, I have to find out quickly if it's in S or not. I realize that storing the known sequences as separate vectors and storing pointers to them in unordered_set is a memory waste, and a trie would be better. But I wanted the constant-time lookup.
A similar case would be a set of string pointers. Each string is really a vector of chars. Now you have a new string s and want to call unordered_set<>::find() repeatedly with a pointer to s.substr(0), s.substr(1), ...
So do you really have a vector, or do you have a set that you are trying to treat like a vector?

l
luiscubal

here it is, obtaining a reference to the coresponding pointer of an iterator use :

example:

string my_str= "hello world";

string::iterator it(my_str.begin());

char* pointer_inside_buffer=&(*it); //<--

[notice operator * returns a reference so doing & on a reference will give you the address].


what happens when the iterator points to my_str.end()? Will the pointer point to an invalid address in the string?
@Unglued: If you try to reference my_str.end() a debugger version will throw an assertion "string iterator not dereferencable" and the release version will throw an exception.
You can obtain the address of the end pointer via &my_str.front() + my_str.size()
b
bk1e

That seems not possible in my situation, since the function I mentioned is the find function of unordered_set.

Are you using custom hash/predicate function objects? If not, then you must pass unordered_set<std::vector<int>*>::find() the pointer to the exact vector that you want to find. A pointer to another vector with the same contents will not work. This is not very useful for lookups, to say the least.

Using unordered_set<std::vector<int> > would be better, because then you could perform lookups by value. I think that would also require a custom hash function object because hash does not to my knowledge have a specialization for vector<int>.

Either way, a pointer into the middle of a vector is not itself a vector, as others have explained. You cannot convert an iterator into a pointer to vector without copying its contents.


Quick answer to your question: Yes, I am using a custom predicate object that decides if two vectors are the same by comparing their elements. (Hmm, that lookup isn't exactly constant-time then ...)
U
Uri

If you can, a better choice may be to change the function to take either an iterator to an element or a brand new vector (if it does not modify).

While you can do this sort of things with arrays since you know how they are stored, it's probably a bad idea to do the same with vectors. &foo[1] does not have the type vector<int>*.

Also, while the STL implementation is available online, it's usually risky to try and rely on the internal structure of an abstraction.


C
Chris Jester-Young

Your function shouldn't take vector<int>*; it should take vector<int>::iterator or vector<int>::const_iterator as appropriate. Then, just pass in foo.begin() + 1.


M
Macke

A vector is a container with full ownership of it's elements. One vector cannot hold a partial view of another, even a const-view. That's the root cause here.

If you need that, make your own container that has views with weak_ptr's to the data, or look at ranges. Pair of iterators (even pointers work well as iterators into a vector) or, even better, boost::iterator_range that work pretty seamlessly.

It depends on the templatability of your code. Use std::pair if you need to hide the code in a cpp.


J
John Dibling

The direct answer to your question is yes. If foo is a vector, you can do this: &foo[1].

This only works for vectors however, because the standard says that vectors implement storage by using contigious memory.

But you still can (and probably should) pass iterators instead of raw pointers because it is more expressive. Passing iterators does not make a copy of the vector.


I think &foo[1] will be an array of integers, not a vector. A vector knows its length. Does &foo[1] know its length?
&foo[1] does not know its length, no. And yes, &foo[1] will be an array of ints.
M
Michael Burr

For example, my vector foo contains (5,2,6,87,251). A function takes vector* and I want to pass it a pointer to (2,6,87,251).

A pointer to a vector<int> is not at all the same thing as a pointer to the elements of the vector.

In order to do this you will need to create a new vector<int> with just the elements you want in it to pass a pointer to. Something like:

 vector<int> tempVector( foo.begin()+1, foo.end());

 // now you can pass &tempVector to your function

However, if your function takes a pointer to an array of int, then you can pass &foo[1].


O
Ogre Psalm33

Use vector::front, it should be the most portable solution. I've used this when I'm interfacing with a fixed API that wants a char ptr. Example:

void funcThatTakesCharPtr(char* start, size_t size);

...

void myFunc(vector<char>& myVec)
{
    // Get a pointer to the front element of my vector:
    char* myDataPtr = &(myVec.front());

    // Pass that pointer to my external API:
    funcThatTakesCharPtr(myDataPtr, myVec.size());
}

1
1800 INFORMATION

If your function really takes vector<int> * (a pointer to vector), then you should pass &foo since that will be a pointer to the vector. Obviously that will not simply solve your problem, but you cannot directly convert an iterator to a vector, since the memory at the address of the iterator will not directly address a valid vector.

You can construct a new vector by calling the vector constructor:

template <class InputIterator> vector(InputIterator, InputIterator)

This constructs a new vector by copying the elements between the two iterators. You would use it roughly like this:

bar(std::vector<int>(foo.begin()+1, foo.end());

But that will pass it a pointer to the vector with content (5,2,6,87,251), whereas I want to pass it a pointer to a vector with content (2,6,87,251).
Yeah I had misread the question. I've updated with some new detail for one possible solution
j
jon-hanson

I haven't tested this but could you use a set of pairs of iterators instead? Each iterator pair would represent the begin and end iterator of the sequence vector. E.g.:

typedef std::vector<int> Seq;
typedef std::pair<Seq::const_iterator, Seq::const_iterator> SeqRange;

bool operator< (const SeqRange& lhs, const SeqRange& rhs)
{
    Seq::const_iterator lhsNext = lhs.first;
    Seq::const_iterator rhsNext = rhs.first;

    while (lhsNext != lhs.second && rhsNext != rhs.second)
        if (*lhsNext < *rhsNext)
            return true;
        else if (*lhsNext > *rhsNext)
            return false;

    return false;
}

typedef std::set<SeqRange, std::less<SeqRange> > SeqSet;

Seq sequences;

void test (const SeqSet& seqSet, const SeqRange& seq)
{
    bool find = seqSet.find (seq) != seqSet.end ();
    bool find2 = seqSet.find (SeqRange (seq.first + 1, seq.second)) != seqSet.end ();
}

Obviously the vectors have to be held elsewhere as before. Also if a sequence vector is modified then its entry in the set would have to be removed and re-added as the iterators may have changed.

Jon


B
BenMorel

Vector is a template class and it is not safe to convert the contents of a class to a pointer : You cannot inherit the vector class to add this new functionality. and changing the function parameter is actually a better idea. Jst create another vector of int vector temp_foo (foo.begin[X],foo.end()); and pass this vector to you functions


C
Community

A safe version to convert an iterator to a pointer (exactly what that means regardless of the implications) and by safe I mean no worries about having to dereference the iterator and cause possible exceptions / errors due to end() / other situations

#include <iostream>
#include <vector>
#include <string.h>

int main()
{
    std::vector<int> vec;

    char itPtr[25];
    long long itPtrDec;
    
    std::vector<int>::iterator it = vec.begin();
    memset(&itPtr, 0, 25);
    sprintf(itPtr, "%llu", it);
    itPtrDec = atoll(itPtr);
    printf("it = 0x%X\n", itPtrDec);
    
    vec.push_back(123);
    it = vec.begin();
    memset(&itPtr, 0, 25);
    sprintf(itPtr, "%llu", it);
    itPtrDec = atoll(itPtr);
    printf("it = 0x%X\n", itPtrDec);
}

will print something like

it = 0x0 it = 0x2202E10

It's an incredibly hacky way to do it, but if you need it, it does the job. You will receive some compiler warnings which, if really bothering you, can be removed with #pragma


H
Hideaki Kazaoka
std::vector<int> v;
  :
auto it=v.end();
auto ptr=v.data()+std::distance(v.begin(),it);

This does not answer the original question. Also, the resulting pointer points past the container.
auto it = v.end (); is just one example, and the code that follows can safely convert an iterator to a pointer. end () should go beyond the scope of the container. You can still safely convert it to a pointer.