2012-04-18 21 views
11

Estoy tratando de insertar una copia de un elemento vector existente para duplicarlo. El siguiente código trabajó en las versiones anteriores, pero no en Visual Studio 2010.¿Cómo insertar un elemento duplicado en un vector?

#include <iostream> 
#include <vector> 

using namespace std; 

int main(int argc, char* argv[]) 
{ 
    vector<int> test; 
    test.push_back(1); 
    test.push_back(2); 
    test.insert(test.begin(), test[0]); 
    cout << test[0] << " " << test[1] << " " << test[2] << endl; 
    return 0; 
} 

salida es -17891602 1 2, espera 1 1 2.

He descubierto por qué está sucediendo: el vector se reasigna y la referencia deja de ser válida antes de copiarse en el punto de inserción. Aparentemente, el antiguo Visual Studio hizo las cosas en un orden diferente, lo que demuestra que un resultado posible del comportamiento indefinido es trabajar correctamente y también probar que nunca es algo en lo que debe confiar.

He encontrado dos formas diferentes de solucionar este problema. Uno es utilizar reserve para asegurarse de que ninguna reasignación tiene lugar:

test.reserve(test.size() + 1); 
    test.insert(test.begin(), test[0]); 

La otra es hacer una copia de la referencia por lo que no hay dependencia de la referencia sigue siendo válido:

template<typename T> 
T make_copy(const T & original) 
{ 
    return original; 
} 

    test.insert(test.begin(), make_copy(test[0])); 

Aunque ambos funcionan, ninguno se siente como una solución natural. ¿Se me escapa algo?

+0

BTW vc11 dev preview da '1 1 2' para el primer ejemplo. –

+0

@Jesse, eso no me sorprende. Se ha seleccionado la sobrecarga Rvalue de 'insert', que parece un error que podrían haber corregido. El código es completamente diferente entre esa sobrecarga y la que toma una referencia constante. –

+0

¿Funciona el casting en int? –

Respuesta

1

Creo que esto es un comportamiento definido. En §23.2.3 del estándar C++ 2011, la tabla 100 lista los requisitos de contenedor de secuencia y hay una entrada para este caso. Se da la expresión ejemplo

a.insert(p,t) 

donde a es un valor de X que es un tipo de contenedor secuencia que contiene elementos de tipo T, p es un iterador const a a, y t es un lvalue o rvalue const de tipo X::value_type , es decir, T.

La afirmación de esta expresión es:

Requiere:T será CopyInsertable en X. Para vector y deque, T también será CopyAssignable.
Efectos: Inserta una copia de t antes de p.

La única cotización específica de vectores relevante es que pude encontrar en §23.3.6.5 el párrafo 1:

Observaciones: Causas reasignación si el nuevo tamaño es mayor que la antigua capacidad. Si no se realiza una reasignación, todos los iteradores y referencias anteriores al punto de inserción permanecen válidos.

Aunque esto menciona que el vector está reasignado, no hace una excepción a los requisitos previos para insert en contenedores de secuencia.

En cuanto a la solución de este problema, estoy de acuerdo con la sugerencia de @ EdChum de simplemente hacer una copia del elemento e insertar esa copia.

+0

No veo nada en su descripción que invoque el caso en el que' t' es una referencia a un miembro de 'a'. –

4

El problema es que vector::insert toma como referencia el valor de un segundo parámetro y no un valor. No necesita la plantilla para hacer una copia, solo use un constructor de copia para crear otro objeto, que se pasará por referencia. Esta copia sigue siendo válida incluso si el vector se redimensiona.

#include <iostream> 
#include <vector> 

using namespace std; 

int main(int argc, char* argv[]) 
{ 
    vector<int> test; 
    test.push_back(1); 
    test.push_back(2); 
    test.insert(test.begin(), int(test[0])); 
    cout << test[0] << " " << test[1] << " " << test[2] << endl; 
    return 0; 
} 
+0

Esto es solo un caso de prueba. Mi código real contiene elementos que son mucho más complicados que un int, y el constructor de un temporal simplemente se pone feo. Buena sugerencia, sin embargo. –

Cuestiones relacionadas