2015-10-08 14 views
5

Ho una funzione template<typename T> che accetta uno const vector<T>&. In detta funzione, ho i vettori cbegin(), cend(), size() e operator[]. Per quanto ho capito, sia lo string che lo vector utilizzano lo spazio contiguo, quindi mi chiedevo se potevo riutilizzare la funzione per entrambi i tipi di dati in modo elegante.Interpreta std :: string come std :: vector di char_type?

Può un std::string essere reinterpretato come std::vector di (appropriato) tipo_carta? Se sì, quali sarebbero i limiti?

+0

Questo è ciò per cui sono stati creati gli iteratori. –

+0

Hai mai pensato di rendere la funzione un modello? – mamahuhu

+0

Affidarsi al comportamento del sottotetto di una classe anziché all'interfaccia pubblicata può essere pericoloso. Qual è la necessità di questo? – Greg

risposta

13

Se si effettua il modello solo per il tipo di const T& e utilizzare i begin(), end(), ecc, funzioni che sia vettoriali e la quota di stringa, allora il vostro codice funzionerà con entrambi i tipi.

+0

Esiste qualche implementazione in cui il codice ** generato ** è condiviso (e non solo il ** codice sorgente **)? – 6502

+0

Cosa intendi per codice generato? – SergeyA

+0

@ 6502: il codice generato non verrà condiviso a meno che gli autori delle librerie standard non si siano messi in difficoltà per farlo accadere. Ma davvero perché ti importa? Quando ottimizzato, le operazioni iteratore e operatore [] compila ciascuna con poche istruzioni. Non è un grosso problema. –

6

Non è garantito il layout di string e vector sarà lo stesso. In teoria potrebbero essere, ma probabilmente non sono in alcuna implementazione comune. Pertanto, non puoi farlo in sicurezza. Vedi la risposta di Zan per una soluzione migliore.

Mi spiego: se io sono un realizzatore libreria standard e decide di implementare std :: string in questo modo ....

template ... 
class basic_string { 
public: 
    ... 
private: 
    CharT* mData; 
    size_t mSize; 
}; 

e decidono di implementare std :: vector in questo modo ...

template ... 
class vector { 
public: 
    ... 
private: 
    T* mEnd; 
    T* mBegin; 
}; 

Quando si reinterpret_cast<string*>(&myVector) si finisce per interpretare il puntatore fino alla fine dei tuoi dati come il puntatore all'inizio dei dati, e il puntatore al punto di partenza dei tuoi dati al formato dei dati. Se il padding tra i membri è diverso, o ci sono membri extra, potrebbe diventare anche più strano e più rotto di quello.

Quindi sì, per fare in modo che questo possa funzionare, entrambi devono memorizzare dati contigui, ma hanno anche bisogno di un bel po 'di altro per essere lo stesso tra le implementazioni per farlo funzionare.

+0

Sia 'stringa' che' vector' usano array contigui per l'archiviazione dei dati. Significa che una funzione non basata su modelli che prende solo gli indirizzi di memoria può essere utilizzata. –

+0

@AndreyNasonov Ti sbagli. Risposta aggiornata per spiegarlo Per favore non fare downvote prima di aver capito :( – David

+0

Non sto parlando di layout del campo e particolare implementazione. Sto parlando di rappresentazione dei dati.Sia 'stringa' che' vector' forniscono la funzione 'data()' che punta al primo elemento. È garantito che è un pezzo di memoria contigua. –

7

Andare in modalità STL e utilizzare gli iteratori. Accetta iteratore per iniziare e iteratore per terminare. Funzionerà con tutti i possibili contenitori, compresi i non contenitori come i flussi.

1

Non è possibile digitare direttamente uno std :: vector in uno std :: string o viceversa. Ma usando gli iteratori che i contenitori STL forniscono, ti permette di iterare sia un vettore che una stringa allo stesso modo. E se la tua funzione richiede l'accesso casuale al contenitore in questione, allora funzionerebbe.

std::vector<char> str1 {'a', 'b', 'c'}; 
std::string str2 = "abc"; 

template<typename Iterator> 
void iterator_function(Iterator begin, Iterator end) 
{ 
    for(Iterator it = begin; it != end; ++it) 
    { 
    std::cout << *it << std::endl; 
    } 
} 

iterator_function(str1.begin(), str1.end()); 
iterator_function(str2.begin(), str2.end()); 

Entrambe le ultime due chiamate di funzione stamperebbero la stessa cosa.

Ora, se si desidera scrivere una versione generica che analizza solo i caratteri memorizzati solo in una stringa o in un vettore, è possibile scrivere qualcosa che ha iterato l'array interno.

void array_function(const char * array, unsigned length) 
{ 
    for(unsigned i = 0; i < length; ++i) 
    { 
    std::cout << array[i] << std::endl; 
    } 
} 

Entrambe le funzioni farebbero la stessa cosa nei seguenti scenari.

std::vector<char> str1 {'a', 'b', 'c'}; 
std::string str2 = "abc"; 

iterator_function(str1.begin(), str1.end()); 
iterator_function(str2.begin(), str2.end()); 
array_function(str1.data(), str1.size()); 
array_function(str2.data(), str2.size()); 

Ci sono sempre diversi modi per risolvere un problema. A seconda di ciò che hai a disposizione, qualsiasi numero di soluzioni potrebbe funzionare. Provare entrambi e vedere quale funziona meglio per la propria applicazione.Se non si conosce il tipo di iteratore, è utile l'iterazione dell'array di tipo char. Se sai che avrai sempre il tipo di modello da passare, il metodo di iterazione del modello potrebbe essere più utile.

2

Se il punto chiave è che si desidera accedere a una porzione continua di memoria in cui le istanze di un determinato tipo char sono memorizzati allora si potrebbe definire la funzione di

void myfunc(const CType *p, int size) { 
    ... 
} 

per far capire che si assumono essi deve essere adiacente in memoria.

Quindi ad esempio per passare il contenuto di un vettore di codice è semplicemente

myfunc(&myvect[0], myvect.size()); 

e per una stringa

myfunc(mystr.data(), mystr.size()); 

o

myfunc(buffer, n); 

per un array.

+0

Tutti parlano di iteratori, stai parlando di puntatori :) – SergeyA

+1

Questo approccio ha un vantaggio: non utilizza i modelli. Ma per favore cambia 'int' in' size_t'. –

+0

@SergeyA: la scrittura di un modello (nelle implementazioni che conosco) condividerà il codice ** sorgente **, generando codice tuttavia distinto per tipi distinti. – 6502

5

std::experimental::array_view<const char>n4512 rappresenta un buffer contiguo di caratteri.

Scrivere your own is not hard e risolve questo problema e (nella mia esperienza) molti altri.

Sia la stringa che il vettore sono compatibili con una vista di matrice.

Questo consente di spostare l'implementazione in un file .cpp (e non esporlo), ti dà le stesse prestazioni di farlo con std::vector<T> const& e, probabilmente, la stessa implementazione, evita la duplicazione di codice, e usa il peso leggero contiguo tipo buffer di cancellazione (che è pieno di gustose parole chiave).

0

Il modo in cui viene posta la domanda al momento è un po 'confuso. Se intendi chiedere "è sicuro trasmettere un tipoa un tipo std::string o viceversa se il vettore contiene valori di char del tipo appropriato?", La risposta è: in nessun modo, non pensare nemmeno a esso! Se stai chiedendo: "Posso accedere alla memoria contigua delle sequenze non vuote di tipo char se sono del tipo std::vector o std::string?" allora la risposta è, sì, è possibile (con la funzione membro data()).