2009-04-13 9 views
31

Tengo un std::vector con n elementos. Ahora necesito pasar un puntero a un vector que tiene los últimos elementos n-1 para una función.¿Convertir iterador en puntero?

Por ejemplo, mi vector<int> foo contiene (5,2,6,87,251). Una función toma vector<int>* y quiero pasarle un puntero a (2,6,87,251).

¿Puedo tomar (con seguridad) el iterador ++foo.begin(), convertirlo en un puntero y pasarlo a la función? O use &foo[1]?

ACTUALIZACIÓN: La gente sugiere que cambie mi función para tomar un iterador en lugar de un puntero. Eso no parece posible en mi situación, ya que la función que mencioné es la función find de unordered_set<std::vector*>. Entonces, en ese caso, ¿está copiando los elementos n-1 de foo en un nuevo vector y llamando al find con un puntero a esa única opción? Muy ineficiente! Es como Shlemiel el pintor, especialmente porque tengo que consultar muchos subconjuntos: los últimos elementos n-1, luego n-2, etc. y ver si están en el unordered_set.

+0

¿Podrías explicar un poco más lo que realmente estás tratando de hacer? Estoy un poco confundido: parece que estás tratando de hacer algo con unordered_set <> y unordered_set <> :: find() que no están realmente diseñados para hacer (pero posiblemente no entiendo) . –

+0

¿Por qué está usando un vector para representar un conjunto desordenado en primer lugar? Si está haciendo consultas de membresía múltiple, hay implementaciones mucho más eficientes. Es posible que desee crear un conjunto y luego consultar en contra de él. – Uri

+0

Tengo un conjunto S de secuencias conocidas. Para cada secuencia nueva, debo averiguar rápidamente si está en S o no. Me doy cuenta de que almacenar las secuencias conocidas como vectores separados y almacenar punteros en ellos en unordered_set es una pérdida de memoria, y un trie sería mejor. Pero quería la búsqueda de tiempo constante. – Frank

Respuesta

8

Eso no parece posible en mi situación, ya que la función que mencioné es la función find de unordered_set<std::vector*>.

¿Está utilizando objetos de función hash/predicado personalizados? De lo contrario, debe pasar unordered_set<std::vector<int>*>::find() el puntero al vector exacto que desea encontrar. Un puntero a otro vector con los mismos contenidos no funcionará. Esto no es muy útil para búsquedas, por decir lo menos.

Usar unordered_set<std::vector<int> > sería mejor, porque entonces podría realizar búsquedas por valor. Creo que también requeriría un objeto de función hash personalizado porque hash no tiene, según mi conocimiento, una especialización para vector<int>.

De cualquier manera, un puntero en el medio de un vector no es en sí mismo un vector, como otros han explicado. No puede convertir un iterador en un puntero a vector sin copiar su contenido.

+0

Respuesta rápida a su pregunta: Sí, estoy usando un objeto predicado personalizado que decide si dos vectores son iguales al comparar sus elementos. (Hmm, esa búsqueda no es exactamente constante, entonces ...) – Frank

0

Si su función realmente toma vector<int> * (un puntero a vector), entonces debe pasar &foo ya que será un puntero al vector. Obviamente, eso no resolverá simplemente su problema, pero no puede convertir directamente un iterador a un vector, ya que la memoria en la dirección del iterador no abordará directamente un vector válido.

Usted puede construir un nuevo vector mediante una llamada al vector constructor:

template <class InputIterator> vector(InputIterator, InputIterator) 

Esto construye un nuevo vector copiando los elementos entre los dos iteradores. Se podría utilizar más o menos así:

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

Pero eso le pasará un puntero al vector con contenido (5,2,6,87,251), mientras que yo quiero pasarle un puntero a un vector con contenido (2,6,87,251). – Frank

+0

Sí, he leído mal la pregunta. He actualizado con algunos detalles nuevos para una posible solución –

5

Si es posible, una mejor opción puede ser cambiar la función de tomar ya sea un iterador a un elemento o un nuevo vector (si no modifica).

Si bien puede hacer este tipo de cosas con arreglos ya que sabe cómo se almacenan, probablemente sea una mala idea hacer lo mismo con los vectores. &foo[1] no tiene el tipo vector<int>*.

Además, aunque la implementación de STL está disponible en línea, suele ser riesgoso intentar confiar en la estructura interna de una abstracción.

+0

Gracias por el comentario. Respondí anteriormente en mi actualización. – Frank

3

Su función no debe tomar vector<int>*; debería tomar vector<int>::iterator o vector<int>::const_iterator según corresponda. Luego, solo pasa el foo.begin() + 1.

1

Por ejemplo, mi vector<int> foo contiene (5,2,6,87,251). Una función toma vector<int>* y quiero pasarle un puntero a (2,6,87,251).

Un puntero a un vector<int> no es en absoluto lo mismo que un puntero a los elementos del vector.

Para hacer esto necesitarás crear una nueva vector<int> con solo los elementos que quieres que pasen un puntero. Algo así como:

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

// now you can pass &tempVector to your function 

Sin embargo, si su función toma un puntero a un array de int, entonces usted puede pasar &foo[1].

2

La respuesta directa a su pregunta es sí. Si foo es un vector, puede hacer esto: & foo [1].

Esto solo funciona para los vectores, sin embargo, porque la norma dice que los vectores implementan el almacenamiento mediante el uso de la memoria contigua.

Pero aún puede (y probablemente debería) pasar iteradores en lugar de punteros sin procesar porque es más expresivo. Pasar iteradores no hace una copia del vector.

+1

Creo que y foo [1] será una matriz de números enteros, no un vector . Un vector conoce su longitud. ¿Conoce & foo [1] su longitud? –

+0

& foo [1] no sabe su longitud, no. Y sí, & foo [1] será una matriz de enteros. –

0

No he probado esto, pero ¿podría usar un conjunto de pares de iteradores? Cada par de iteradores representaría el iterador de inicio y final del vector de secuencia. Por ejemplo:

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(); 
} 

Obviamente, los vectores tienen que mantenerse en otro lugar como antes. Además, si se modifica un vector de secuencia, entonces su entrada en el conjunto debería eliminarse y volverse a agregar ya que los iteradores pueden haber cambiado.

Jon

3

Un vector es un recipiente con la plena propiedad de que de elementos. Un vector no puede contener una vista parcial de otro, incluso una vista const. Esa es la causa raíz aquí.

Si lo necesita, haga su propio contenedor que tenga vistas con weak_ptr's a los datos, o mire los rangos. Un par de iteradores (incluso los punteros funcionan bien como iteradores en un vector) o, mejor aún, boost :: iterator_range que funcionan bastante bien.

Depende de la capacidad de tentación de su código. Use std :: pair si necesita ocultar el código en una cpp.

62

aquí está, la obtención de una referencia al puntero coresponding de un uso iterador:

ejemplo:

string my_str= "hello world"; 

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

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

[Aviso operador * devuelve una referencia hacerlo & en una referencia dará usted la dirección].

+11

¿Qué sucede cuando el iterador apunta a '' 'my_str.end()' ''? ¿Señalará el puntero a una dirección inválida en la cadena? – Unglued

+5

@Unglued: Si intenta hacer referencia a 'my_str.end()', una versión del depurador arrojará una aserción '" iterador de cadena no referenciable '' y la versión lanzará una excepción. – ahmd0

1

Use vector::front, debería ser la solución más portátil. Lo he usado cuando estoy interactuando con una API fija que quiere un char ptr. Ejemplo:

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()); 
} 
0

Vector es una clase de plantilla y no es seguro para convertir el contenido de una clase a un puntero: Usted no puede heredar la clase de vectores para agregar esta nueva funcionalidad. y cambiar el parámetro de la función es en realidad una mejor idea. Jst crea otro vector de int vector temp_foo (foo.begin [X], foo.end()); y pase este vector a usted funciones

0

Una versión segura para convertir un iterador en un puntero (exactamente lo que eso significa sin importar las implicaciones) y seguro me refiero a no tener preocupaciones sobre tener que desreferenciar el iterador y causar posibles excepciones/errores debidos a end()/otras situaciones

#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); 
} 

se imprimirán algo así como

it = 0x0

it = 0x2202E10

Es una forma increíblemente hacky de hacerlo, pero si lo necesita, hace el trabajo. Recibirás algunas advertencias del compilador que, si realmente te molestan, se pueden eliminar con #pragma

Cuestiones relacionadas