2009-05-23 7 views
10

Questo compila:STL assegnazione contenitore e puntatori const

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

Questo non lo fa:

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

Quali sono le regole di equivalenza di tipo per i puntatori const annidati? Pensavo che la conversione sarebbe stata implicita. Inoltre, preferirei non implementare l'assegnazione point-wise dei container STL, a meno che non sia necessario.

risposta

44

L'assegnazione diretta non è possibile. Come altri hanno spiegato, l'equivalenza non è stabilita dai tipi di puntatore, ma dai tipi di contenitore. In questo caso, il vettore non vuole accettare un altro vettore con un tipo di elemento diverso, ma compatibile.

Nessun vero problema, dal momento che è possibile utilizzare la funzione di membro assign:

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

Perché? Comprenderei essere titubante riguardo alla conversione implicita del vettore nel vettore , ma int * in const int *? Pensavo che const ricevesse un trattamento speciale a questo riguardo. Qualche idea sul perché lo standard C++ abbia deciso contro di esso? –

+0

Forse qualcosa a che fare con i modelli sono esattamente uguali, non sempre il più conveniente. – user7116

+10

Ho visto questo esempio da qualche parte: una mela è un frutto, ma un sacchetto di mele non è un sacchetto di frutta. Violarebbe il Principio di sostituibilità di Liskov: puoi mettere un'arancia in un sacchetto di frutta, ma non puoi mettere un'arancia in un sacchetto di mele. Se Bag (o, nel tuo caso, vector ) fosse immutabile, non avresti questo problema. –

7

Il problema non sono i puntatori, ma i tipi dei due vettori. Non ci sono conversioni standard tra tipi di modelli come quelli di v1 e v2 nel tuo esempio.

Questo è forse più facile da vedere nel seguente codice:

#include <vector> 
using namespace std; 

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

La conversione da int* a const int* è costruito nella lingua, ma vettori di questi hanno alcuna conversione automatica da uno all'altro.

3

Sarebbe perfettamente possibile scrivere la propria versione di vector in cui questo è stato possibile. Potrebbe essere identico al tipo standard, ma con una versione templated di operator=, qualcosa di simile:

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

dove T è il tipo di elemento di tutta la classe, mentre A è qualsiasi tipo assegnabile T.

Non mi è chiaro il motivo per cui lo std::vector non ha questo.

4

in C++ classi templated, ogni esemplificazione del modello è una classe completamente diversa - non v'è molta differenza tra come vector<int *> e vector<const int *> in quanto v'è tra il vector<int *> e vector<string> o altre due classi per quella materia.

E 'possibile che il comitato avrebbe potuto aggiungere un operatore di conversione su vector-vector<U> come Earwicker suggerisce - e si può andare avanti e fornire la propria implementazione di tale funzione:

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

e utilizzarlo come così:

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

Purtroppo, fino C++ 0x viene fornito con i costruttori la sua mossa, questo sarà piuttosto male prestazioni-saggio.

1

Coercion by Member Template idioma è un possibile approccio per risolvere il problema. In sostanza, viene aggiunto un operatore di assegnazione copia modello membro che consente alla classe template di partecipare alle stesse conversioni di tipo implicito (coercizione) che sono altrimenti possibili solo sui parametri di tipo del modello di classe.Sebbene l'idioma sia utilizzato nell'STL in altri luoghi, non è disponibile in std :: vector.

2

pericoloso, a meno che non si conoscono i tipi sono assolutamente compatibili:

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

maggior parte delle implementazioni STL fanno utilizzare una specializzazione erano tutti i vettori di puntatori condividono la stessa implementazione sottostante. Questo perché (void *) ha tipicamente le stesse dimensioni di (int *) o qualsiasi altro tipo di puntatore.

2

Un punto importante non menzionato in nessuna delle precedenti risposte è che le specializzazioni dei modelli rendono impossibile l'implementazione su basi linguistiche. Consideriamo:

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

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

Così Test<const int> contiene una serie di caratteri, che contiene Test<int> un singolo 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; 
} 

In realtà vector<foo *> e vector<const foo *> difficilmente possono variare a tutti --- in particolare, possono avere la stessa dimensione. Tuttavia, la possibilità di specializzazione template esplicita significa che potrebbero variare in modo spettacolare , quindi la riluttanza del compilatore per consentire la conversione.

(Questa risposta viene principalmente copiata da http://bytes.com/topic/c/answers/449611-cast-vector-foo-vector-const-foo#post1717570)