2009-05-23 19 views
10

Esto compila:STL asignación de contenedores y punteros const

int* p1; 
const int* p2; 
p2 = p1; 

Esto no es así:

vector<int*> v1; 
vector<const int*> v2; 
v2 = v1; // Error! 
v2 = static_cast<vector<const int*> >(v1); // Error! 

¿Cuáles son las reglas de equivalencia de tipo de punteros const anidados? Pensé que la conversión sería implícita. Además, preferiría no implementar la asignación por puntos de los contenedores STL, a menos que realmente tenga que hacerlo.

Respuesta

44

La asignación directa no es posible. Como otros explicaron, la equivalencia no está establecida por los tipos de puntero, sino por los tipos de contenedor. En este caso, el vector no quiere aceptar otro vector que tenga un tipo de elemento diferente, pero compatible.

No hay problema real, ya que se puede utilizar la función de miembro de assign:

v2.assign(v1.begin(), v1.end()); 
+2

¿Por qué? Yo entendería dudar sobre la conversión implícita del vector al vector , pero int * to const int *? Pensé que const recibe un trato especial en este sentido. ¿Alguna idea de por qué el estándar C++ decidió no hacerlo? –

+0

Quizás algo relacionado con las plantillas coincida exactamente, no siempre es el más conveniente. – user7116

+10

Vi este ejemplo en alguna parte: una manzana es una fruta, pero una bolsa de manzanas no es una bolsa de fruta. Violaría el Principio de Sustituibilidad de Liskov: puedes poner una naranja en una bolsa de fruta, pero no puedes poner una naranja en una bolsa de manzanas. Si Bag (o, en su caso, vector ) fuera inmutable, no tendría este problema. –

7

El problema no son los punteros, sino los tipos de los dos vectores. No hay conversiones estándar entre tipos de plantilla como los de v1 y v2 en su ejemplo.

Esta es quizás más fácil de ver en el siguiente código:

#include <vector> 
using namespace std; 

int main() { 
    vector <char> cv; 
    vector <int> iv; 
    cv = iv; // error 
} 
20

La conversión de int* a const int* está integrado en el lenguaje, pero los vectores de estos tienen ninguna conversión automática de una a la otra.

3

sería perfectamente posible escribir su propia versión de vector donde esto era posible. Sería idéntico al tipo estándar, pero con una versión de plantilla de operator=, algo como esto:

template <class A> 
vector2<T> &operator=(const vector2<A> &other) 
{ 
    assign(other.begin(), other.end()); 
    return *this; 
} 

donde t es el tipo de elemento de toda la clase, mientras que A es cualquier tipo asignables a T.

No tengo claro por qué std::vector no tiene esto.

4

en C++ clases con plantilla, cada instanciación de la plantilla es una clase completamente diferente - hay tanta diferencia entre vector<int *> y vector<const int *> como la hay entre vector<int *> y vector<string> o cualquier otras dos clases para el caso.

Es posible que el comité podría haber añadido un operador de conversión de vector-vector<U> como Earwicker sugiere - y usted puede seguir adelante y proporcionar su propia implementación de una función de este tipo:

template <class A, class T> 
vector<T> convert_vector(const vector<A> &other) 
{ 
    vector<T> newVector; 
    newVector.assign(other.begin(), other.end()); 
    return newVector; 
} 

y utilizarlo como por lo que:

vector<int*> v1; 
vector<const int*> v2; 
v2 = convert_vector<const int*>(v1); 

Por desgracia, hasta C++ 0x viene con constructores que el movimiento, esto va a ser bastante malo en cuanto al rendimiento.

1

Coercion by Member Template idioma es un posible enfoque para resolver el problema. Básicamente, se agrega un operador de asignación de copia de plantilla de miembro que permite que la clase de plantilla participe en las mismas conversiones de tipo implícito (coerción) que de otro modo solo serían posibles en los parámetros de tipo de la plantilla de clase.Aunque la expresión idiomática se usa en el STL en otros lugares, no está disponible en std :: vector.

2

peligroso, a menos que conozca los tipos son absolutamente compatibles:

v2 = reinterpret_cast<std::vector<const int *> & >(v1);

mayoría de las implementaciones STL hacen uso de una especialización eran todos los vectores de punteros comparten la misma implementación subyacente. Esto es porque (void *) suele tener el mismo tamaño que (int *) o cualquier otro tipo de puntero.

2

Un punto importante que no se menciona en ninguna de las respuestas anteriores es que las especializaciones de plantilla hacen que esto sea imposible de implementar en una base de todo el idioma. Considerar:

template<class T> 
class Test 
{ 
    T t; 
}; 

template<> 
class Test<const int> 
{ 
    char array[1000]; 
}; 

Así Test<const int> contiene una matriz de caracteres, mientras que Test<int> contiene un solo int.

#include <iostream> 
using namespace std; 

int main() 
{ 
    Test<int> t1; 
    Test<const int> t2; 
    cout << sizeof(t1) << endl; // gives 4 
    cout << sizeof(t2) << endl; // gives 1000 
    return 0; 
} 

En realidad vector<foo *> y vector<const foo *> difícilmente pueden ser diferentes en todo --- en particular, pueden tener el mismo tamaño. Sin embargo, la posibilidad de especialización explícita de la plantilla significa que podrían diferir espectacularmente, de ahí la reticencia del compilador para permitir la conversión.

(Esta respuesta se copia en su mayoría de http://bytes.com/topic/c/answers/449611-cast-vector-foo-vector-const-foo#post1717570)

Cuestiones relacionadas