Jak skonstruować std :: string ze std :: vector <char>?

115

Oprócz (oczywistego) zbudowania najpierw łańcucha w stylu C, a następnie użycia go do stworzenia std :: string, czy istnieje szybszy / alternatywny / „lepszy” sposób inicjalizacji łańcucha z wektora znaków?

oompahloompah
źródło
4
Czy można to zrobić bez kopiowania? Innymi słowy, coś, co robi to samo, std::vector<char> v2(std::move(v))ale z a std::stringjako nowym obiektem.
tobi_s

Odpowiedzi:

197

Cóż, najlepszym sposobem jest użycie następującego konstruktora:

template<class InputIterator> string (InputIterator begin, InputIterator end);

co doprowadziłoby do czegoś takiego:

std::vector<char> v;
std::string str(v.begin(), v.end());
Greg
źródło
41

Myślę, że możesz to zrobić

std::string s( MyVector.begin(), MyVector.end() );

gdzie MyVector jest twoim std :: vector.

LiMuBei
źródło
1
Z wyjątkiem VS2013, który w czasie wykonywania potwierdza nieprawidłowe iteratory, chyba że ustawisz _ITERATOR_DEBUG_LEVEL=1(w takim przypadku wydaje się działać dobrze).
Cameron,
35

Z C ++ 11, można to zrobić std::string(v.data())albo, jeśli wektor nie zawiera '\0'na końcu std::string(v.data(), v.size()).

Ștefan
źródło
Uważam, że nawet w C ++ 98 można zrobić std :: string (& v [0]). Dzieje się tak, oczywiście, jeśli wektor jest zakończony zerem.
Jamie
1
@Jamie: Nawiasem mówiąc, w C ++ 98 string(&v[0], v.size())powinno również działać, ale tylko później assert(not v.empty());, ponieważ jeśli wektor jest pusty, oba v[0]i v.front()wywołałyby niezdefiniowane zachowanie. To, poza prostotą składni, polegającą na tym, że nie trzeba używać operatora address-of, jest prawdziwą zaletą funkcji C ++ 11 data(), która działa nawet na pustym wektorze.
Adam H. Peterson
Bardzo prawdziwe. Niestety, mój projekt utknął w Visual Studio 2008 w najbliższej przyszłości. Dobrze o sprawdzeniu najpierw długości wektora.
Jamie
Jeśli wektor nie zawiera '\ 0', std::string(v.data())może prowadzić do dłuższego ciągu. Więc nie używaj w ten sposób.
heLomaN
@heLomaN: Co jest nie tak z tym std::string(v.data(), v.size()), co zostało wyraźnie wymienione w odpowiedzi właśnie z tego powodu?
Sz.
12
std::string s(v.begin(), v.end());

Gdzie v jest praktycznie dowolną iteracją. (W szczególności begin () i end () muszą zwracać InputIterators.)

Martin Stone
źródło
3

Dla kompletności, inny sposób to std::string(&v[0])(chociaż musisz upewnić się, że twój łańcuch jest zakończony znakiem null i std::string(v.data())generalnie jest preferowany.

Różnica polega na tym, że możesz użyć poprzedniej techniki, aby przekazać wektor do funkcji, które chcą zmodyfikować bufor, czego nie można zrobić za pomocą .data ().

Zamieszki
źródło
Dlaczego nie możesz data()zmodyfikować bufora? Wydaje mi się, że jeśli wywołasz data()wektor inny niż const, zwróci on a T *(a jeśli twój wektor to const, 'v [0]' i T const &tak zwróci a ).
Adam H. Peterson
@ AdamH.Peterson wydaje się, że jest to przeoczenie w standardzie. Dane Std :: vector mają zarówno funkcje const, jak i non-const, podczas gdy w obecnym standardzie std :: string ma tylko funkcję const: en.cppreference.com/w/cpp/string/basic_string/data Zauważ, że C ++ 17 dodaje wersję inną niż stała, więc może to nie być w nieskończoność - jednak obecny standard stwierdza, że ​​„Modyfikowanie tablicy znaków, do której uzyskiwany jest dostęp poprzez dane, ma niezdefiniowane zachowanie”.
Riot
Byłoby to prawdą, gdyby vbył ciągiem, ale typ docelowy jest ciągiem i vjest wektorem. (Ale fajnie jest widzieć, że C ++ 17 da ciągi parzystość z wektorem.)
Adam H. Peterson
@ AdamH.Peterson masz oczywiście rację. Na to pytanie odpowiedziałem jakiś czas temu, założyłem, że całe pytanie dotyczy wyłącznie strun bez sprawdzania.
Riot
2

Podoba mi się odpowiedź Stefana (wrzesień 11 '13), ale chciałbym ją nieco wzmocnić:

Jeśli wektor kończy się terminatorem zerowym, nie powinieneś używać (v.begin (), v.end ()): powinieneś używać v.data () (lub & v [0] dla tych sprzed C ++ 17) .

Jeśli v nie ma terminatora zerowego, należy użyć (v.begin (), v.end ()).

Jeśli użyjesz funkcji begin () i end (), a wektor ma kończące zero, otrzymasz na przykład łańcuch „abc \ 0”, który ma długość 4, ale w rzeczywistości powinien mieć tylko „abc”.

Henri Raath
źródło
1
vector<char> vec;
//fill the vector;
std::string s(vec.begin(), vec.end());
TechCat
źródło
Czy mógłbyś dodać kilka wyjaśnień wraz z kodem?
Paul Floyd
1
Szablon biblioteki ciągów C ++ 11 wygląda następująco default (1) string (); copy (2) string (const string & str); substring (3) string (const string & str, size_t pos, size_t len ​​= npos); z c-string (4) string (const char * s); z bufora (5) string (const char * s, size_t n); fill (6) string (size_t n, char c); range (7) template <class InputIterator> string (najpierw InputIterator, ostatni InputIterator); lista inicjalizacyjna (8) string (initializer_list <char> il); move (9) string (string && str) noexcept; postępujemy zgodnie z 7. szablonem.
TechCat
Cześć @TechCat! Przeczytaj, jak napisać dobrą odpowiedź, zanim odpowiesz na następne pytanie. Miłego pobytu w SO!
Diggy.
Czy mógłbyś edytować swoją odpowiedź wraz z opisem?
Paul Floyd