Jak zainicjować std :: vector z tablicy w stylu C?


174

Jaki jest najtańszy sposób inicjalizacji a std::vectorz tablicy w stylu C?

Przykład: W poniższej klasie mam vector, ale z powodu zewnętrznych ograniczeń dane zostaną przekazane jako tablica w stylu C:

class Foo {
  std::vector<double> w_;
public:
  void set_data(double* w, int len){
   // how to cheaply initialize the std::vector?
}

Oczywiście mogę wywołać, w_.resize()a następnie zapętlić elementy lub wywołać std::copy(). Czy są jakieś lepsze metody?


11
Sedno problemu polega na tym, że wektor nie ma sposobu, aby wiedzieć, czy ten sam alokator został użyty do utworzenia tablicy w stylu C. Jako taki wektor musi przydzielać pamięć przy użyciu własnego alokatora. W przeciwnym razie może po prostu zamienić podstawową tablicę i zastąpić ją twoją tablicą.
Nieważne

Odpowiedzi:


233

Nie zapominaj, że możesz traktować wskaźniki jako iteratory:

w_.assign(w, w + len);

3
To kwestia jakości implementacji. Ponieważ iteratory mają znaczniki, które określają ich kategorie, implementacja z assignpewnością może ich używać do optymalizacji; przynajmniej w VC ++ robi właśnie to.
Pavel Minaev

38
Szybkim rozwiązaniem mogłoby być std :: vector <double> w_ (w, w + len);
jamk

1
To kopiuje elementy do nowo utworzonego magazynu dla „w_”; „w_.data” nie będzie wskazywać na „w”. Nadal musisz cofnąć przydział „w”. Nie ma przeniesienia własności
Indy9000

1
Jeśli jest jeden element za końcem, powinno być w porządku (tak jak v.end()iterator wskazujący jeden za koniec z wektorem w podobnym przypadku). Jeśli otrzymasz asercję, oznacza to, że coś jest nie tak.
Pavel Minaev

1
Czy to szybko zwolni pamięć tablicy, gdy wektor znajdzie się poza zakresem?
Adrian Koch

41

Używasz słowa inicjalizuj, więc nie jest jasne, czy jest to przypisanie jednorazowe, czy może się zdarzyć wiele razy.

Jeśli potrzebujesz tylko jednorazowej inicjalizacji, możesz umieścić ją w konstruktorze i użyć konstruktora wektorów iteratorowych:

Foo::Foo(double* w, int len) : w_(w, w + len) { }

W przeciwnym razie użyj przypisania zgodnie z wcześniejszą sugestią:

void set_data(double* w, int len)
{
    w_.assign(w, w + len);
}

1
W moim przypadku zadanie będzie się powtarzać.
Frank

12

Możesz `` nauczyć się '' rozmiaru tablicy automatycznie:

template<typename T, size_t N>
void set_data(const T (&w)[N]){
    w_.assign(w, w+N);
}

Mamy nadzieję, że możesz zmienić interfejs na set_data jak powyżej. Nadal akceptuje tablicę w stylu C jako pierwszy argument. Po prostu bierze to przez odniesienie.


Jak to działa

[Aktualizacja: zobacz tutaj, aby uzyskać bardziej kompleksową dyskusję na temat poznania rozmiaru]

Oto bardziej ogólne rozwiązanie:

template<typename T, size_t N>
void copy_from_array(vector<T> &target_vector, const T (&source_array)[N]) {
    target_vector.assign(source_array, source_array+N);
}

Działa to, ponieważ tablica jest przekazywana jako odniesienie do tablicy. W C / C ++ nie możesz przekazać tablicy jako funkcji, zamiast tego rozpadnie się ona na wskaźnik i stracisz rozmiar. Ale w C ++ możesz przekazać referencję do tablicy.

Przekazywanie tablicy przez odwołanie wymaga dokładnego dopasowania typów. Rozmiar tablicy jest częścią jej typu. Oznacza to, że możemy użyć parametru szablonu N, aby poznać za nas rozmiar.

Jeszcze prostsze może być posiadanie funkcji zwracającej wektor. Po zastosowaniu odpowiednich optymalizacji kompilatora powinno to być szybsze niż się wydaje.

template<typename T, size_t N>
vector<T> convert_array_to_vector(const T (&source_array)[N]) {
    return vector<T>(source_array, source_array+N);
}

1
W ostatniej próbce return { begin(source_array), end(source_array) };jest również możliwe
MM

12

Cóż, Pavel był blisko, ale istnieje jeszcze prostsze i bardziej eleganckie rozwiązanie do inicjalizacji kontenera sekwencyjnego z tablicy w stylu ac.

W Twoim przypadku:

w_ (array, std::end(array))
  • tablica da nam wskaźnik do początku tablicy (nie złapał jej nazwy),
  • std :: end (tablica) dostarczy nam iterator do końca tablicy.

1
Co obejmuje / wersja C ++ wymaga tego?
Vlad

1
Jest to jeden z konstruktorów std :: vector od co najmniej c ++ 98… Nazywa się to „konstruktorem zakresu”. cplusplus.com/reference/vector/vector/vector Wypróbuj.
Mugurel

1
Bardziej niezależna wersja to: w_ (std :: begin (tablica), std :: end (tablica)); (W przyszłości możesz zmienić tablicę C na kontener C ++).
Andrew Romanov

13
Pamiętaj, że działa to tylko wtedy, gdy masz rzeczywistą array(co zwykle oznacza, że ​​kopiujesz z tablicy globalnej lub lokalnej (zadeklarowanej w bieżącej funkcji)). W przypadku OP otrzymuje wskaźnik i długość, a ponieważ nie ma szablonu na długości, nie mogą zmienić na otrzymanie wskaźnika do tablicy o rozmiarze lub czegokolwiek, więc std::endnie zadziała.
ShadowRanger

2
vectornie przeciąża operator(), więc to się nie skompiluje. std::endwywołanie ze wskaźnika też nie ma sensu (pytanie dotyczy przypisania wektora ze wskaźnika i oddzielnej zmiennej długości). Poprawiłoby twoją odpowiedź, pokazując więcej kontekstu na temat tego, co próbujesz zasugerować
MM

2

Szybka ogólna odpowiedź:

std::vector<double> vec(carray,carray+carray_size); 

lub konkretne pytanie:

std::vector<double> w_(w,w+len); 

w oparciu o powyższe : Nie zapominaj, że możesz traktować wskaźniki jak iteratory


0

std::vector<double>::assignjest drogą do zrobienia, ponieważ jest to mały kod . Ale jak to właściwie działa? Czy nie zmienia rozmiaru, a następnie kopiuje? W implementacji STL w MS używam go dokładnie tak.

Obawiam się, że nie ma szybszego sposobu na zaimplementowanie (ponownej) inicjalizacji std::vector.


co, jeśli dane mają być udostępniane między wektorem a tablicą? Czy w tym przypadku musimy cokolwiek kopiować?
Vlad

czy to odpowiedź czy pytanie? co to wnosi do już istniejących odpowiedzi?
Jean-François Fabre

@ Jean-FrançoisFabre i co przynosi Twój komentarz? ;) prawda, to kiepska odpowiedź udzielona wieki temu.
Janusz Lenar
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.