jak zainicjować 'const std :: vector <T>' jak tablica ac

79

Czy jest eleganckim sposobem tworzenia i zainicjować const std::vector<const T>podobny const T a[] = { ... }do stałej (i małych) liczba wartości?
Muszę często wywoływać funkcję, która oczekuje a vector<T>, ale te wartości nigdy się nie zmienią w moim przypadku.

W zasadzie pomyślałem o czymś takim

namespace {
  const std::vector<const T> v(??);
}

ponieważ v nie będzie używane poza tą jednostką kompilacji.

vscharf
źródło

Odpowiedzi:

61

Musisz albo poczekać na C ++ 0x, albo użyć czegoś takiego jak Boost.Assign, aby to zrobić.

na przykład:

#include <boost/assign/std/vector.hpp>
using namespace boost::assign; // bring 'operator+=()' into scope

vector<int> v;
v += 1,2,3,4,5;

dla C ++ 11:

vector<int> luggage_combo = { 1, 2, 3, 4, 5 };
Ferruccio
źródło
1
„Muszę” może być nieco mocne; podejście dwuetapowe może również działać. Chociaż podejście doładowania jest z pewnością dobre.
stycznia
Geussing miałeś na myśli: #include <boost / assign / std / vector.hpp>
Zac
1
@Jason: boost. assign definiuje szablonowe przeciążenia operatora + = i operatora na wektorze <T>
Ferruccio
ale jak przechwycić każdą z wartości przecinków?
Jason S
41
12345? Mam tę samą kombinację na moim bagażu!
JRG,
39

Jeśli pytasz, jak zainicjować wektor const, aby miał interesującą zawartość, to prawdopodobnie odpowiedzią jest użycie konstruktora kopiującego. Najpierw mozolnie wypełniasz wektor, a następnie tworzysz z niego nowy wektor const. Lub możesz użyć vector<InputIterator>(InputIterator, InputIterator)szablonu konstruktora do zainicjowania z innego rodzaju kontenera lub tablicy. Jeśli tablica, to można ją zdefiniować za pomocą listy inicjalizacyjnej.

Mam nadzieję, że coś takiego jest bliskie temu, czego chcesz:

const T ra[3] = {t1, t2, t3};
const vector<const T> v(ra, ra+3);

Jeśli pytasz, jak przekazać wektor const do funkcji, która przyjmuje wektor, odpowiedź brzmi:

  • nie możesz, ponieważ funkcja może zmienić wektor, a twój obiekt / odniesienie jest const. Utwórz kopię oryginału niebędącą stałą i przekaż ją.

lub

  • użyj const_castdo usunięcia stałej, aby przekazać ją do funkcji, która pobiera wektor inny niż stała, ale o której wiesz, że nie zmodyfikuje wektora.

Ta ostatnia jest jedną z tych rzeczy, które całkiem słusznie sprawią, że każdy, kto to zobaczy, będzie komentował gogle i fakt, że nic nie robią. Właśnie po to const_castjest, ale istnieje dość silny argument, który mówi, że jeśli potrzebujesz const_cast, już przegrałeś.

Robienie obu tych rzeczy (tworzenie wektora const z innego niż const za pomocą konstruktora kopiującego, a następnie odrzucanie stałej) jest zdecydowanie złe - powinieneś był po prostu użyć wektora innego niż const. Więc wybierz co najwyżej jedno z nich do zrobienia ...

[ Edycja: właśnie zauważyłem, że mówisz o różnicy między vector<T>i const vector<const T>. Niestety w STL vector<const T>i vector<T>są całkowicie niepowiązane typy, a jedynym sposobem na konwersję między nimi jest kopiowanie. To jest różnica między wektorami a tablicami - a T**można dyskretnie i bezpiecznie przekonwertować na const T *const *]

Steve Jessop
źródło
Jeśli nie chcę kopiować t1, t2, t3, jak mogę to zrobić najlepiej? To jedyne / najlepsze rozwiązanie, jakie do tej pory znalazłem: <code> const int * p = & ra [0]; wektor <const int *> v; for (int i = 0; i <size; ++ i, ++ p) v.push_back (p); </code>
AudioDroid
@AudioDroid: tak, nie ma możliwości umieszczenia czegokolwiek w wektorze bez kopiowania (lub przenoszenia, w C ++ 11), więc najbliższy jest wektor wskaźników (lub inteligentnych wskaźników). Ale jeśli masz już swoje trzy obiekty w tablicy, zwykle nie ma sensu tworzyć wektora wskaźników do nich: po prostu użyj tablicy do tego, do czego użyłbyś wektora.
Steve Jessop
15

Krótki i brudny sposób (podobny do Boost list_of())

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
using namespace std;

template <typename T>
struct vlist_of : public vector<T> {
    vlist_of(const T& t) {
        (*this)(t);
    }
    vlist_of& operator()(const T& t) {
        this->push_back(t);
        return *this;
    }
};

int main() {
    const vector<int> v = vlist_of<int>(1)(2)(3)(4)(5);
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, "\n"));
}

Teraz C ++ 11 ma listy inicjatorów, więc nie musisz tego robić w ten sposób ani nawet używać Boost. Ale, jako przykład, możesz wykonać powyższe czynności w C ++ 11 wydajniej w ten sposób:

    #include <iostream>
    #include <vector>
    #include <utility>
    #include <ostream>
    using namespace std;

    template <typename T>
    struct vlist_of : public vector<T> {
        vlist_of(T&& t) {
            (*this)(move(t));
        }
        vlist_of& operator()(T&& t) {
            this->push_back(move(t));
            return *this;
        }
    };

    int main() {
        const vector<int> v = vlist_of<int>(1)(2)(3)(4)(5);
        for (const auto& i: v) {
            cout << i << endl;
        }
    }

Ale nadal nie jest tak wydajne, jak używanie listy inicjalizującej C ++ 11, ponieważ nie ma operator=(vlist_of&&)zdefiniowanej dla wektora.

Sposób tjohns20 zmodyfikowany jak poniżej może być lepszym C ++ 11 vlist_of:

#include <iostream>
#include <vector>
#include <utility>
using namespace std;

template <typename T>
class vlist_of {
    public:
        vlist_of(T&& r) {
            (*this)(move(r));
        }
        vlist_of& operator()(T&& r) {
            v.push_back(move(r));
            return *this;
        }
        vector<T>&& operator()() {
            return move(v);
        }
    private:
        vector<T> v;
    
};

int main() {
    const auto v = vlist_of<int>(1)(2)(3)(4)(5)();
    for (const auto& i : v) {
        cout << i << endl;
    }
    
}
Cień2531
źródło
niegodziwy, ale przydatny do testowania itp. ++
Eli Bendersky
12

Jak powiedzieli inni, nie możesz zainicjować wektora w ten sam sposób, w jaki możesz zainicjować tablicę w stylu C, chyba że podasz mu wskaźniki do tablicy źródłowej. Ale w takim przypadku, jeśli twój wektor jest globalną stałą, dlaczego nie użyć zamiast tego starej tablicy w stylu C.

const int MyInts[] = {
1, 2, 3, 4, 5};

const size_t NumMyInts = sizeof(MyInts)/sizeof(MyInts[0]);

Możesz nawet użyć algorytmów STL przeciwko tej tablicy, w taki sam sposób, w jaki używałbyś algorytmów do wektora const ...

const int* myInt = std::find( &MyInts[0], &MyInts[NumMyInts], 3);
John Dibling
źródło
4
To dobra uwaga, ale przekazuje to do funkcji, która oczekuje wektora. Może nie być w stanie zmodyfikować tej funkcji.
Ferruccio
Wymaga to zewnętrznego śledzenia wielkości wektora, co jest uciążliwe w przypadku wielu wektorów. W przeciwnym razie to rozwiązanie byłoby najlepsze, ponieważ wymaga tylko jednej kopii liczb całkowitych w pamięci.
MasterHD
6

Możesz to zrobić w dwóch krokach:

namespace {
    const T s_actual_array[] = { ... };
    const std::vector<const T> s_blah(s_actual_array,
        s_actual_array + (sizeof(s_actual_array) / sizeof(s_actual_array[0])));
}

Może nie tak piękne, jak byś chciał, ale funkcjonalne.

janm
źródło
5

Co powiesz na:

int ar[]={1,2,3,4,5,6};
const int TotalItems = sizeof(ar)/sizeof(ar[0]);
std::vector<int> v(ar, ar+TotalItems);
opal
źródło
Nieważne, „Steve Jessop” już wskazał na to podejście.
opal
3

Stare pytanie, ale spotkałem się dzisiaj z tym samym problemem, oto podejście, które było najbardziej akceptowalne dla moich celów:

vector<int> initVector(void)
{
    vector<int> initializer;
    initializer.push_back(10);
    initializer.push_back(13);
    initializer.push_back(3);
    return intializer;
}

int main()
{
    const vector<int> a = initVector();
    return 0;
}

Przykład unikania nadmiernego kopiowania:

vector<int> & initVector(void)
{
    static vector<int> initializer;
    if(initializer.empty())
    {
        initializer.push_back(10);
        initializer.push_back(13);
        initializer.push_back(3);
    }
    return intializer;
}

int main()
{
    const vector<int> & a = initVector();
    return 0;
}
Kevin
źródło
0

Jeśli wszystkie są takie same, możesz po prostu zrobić

vector<T> vec(num_items, item);

ale zakładam, że tak nie jest - w takim przypadku najładniejszy sposób to prawdopodobnie:

vector<T> vec(num_items);
vec[0] = 15;
vec[1] = 5;
...

C ++ 0x pozwoli ci użyć listy inicjalizatorów dokładnie tak, jak myślisz, ale niestety nie jest to zbyt dobre w tej chwili.

Piotr
źródło
0

Na podstawie odpowiedzi Shadow2531 używam tej klasy do inicjalizacji wektorów, bez dziedziczenia po std :: vector, tak jak to zrobiło rozwiązanie Shadow

template <typename T>
class vector_init
{
public:
    vector_init(const T& val)
    {
        vec.push_back(val);
    }
    inline vector_init& operator()(T val)
    {
        vec.push_back(val);
        return *this;
    }
    inline std::vector<T> end()
    {
        return vec;
    }
private:
    std::vector<T> vec;
};

Stosowanie:

std::vector<int> testVec = vector_init<int>(1)(2)(3)(4)(5).end();

W porównaniu z rozwiązaniem Steve'a Jessopa tworzy o wiele więcej kodu, ale jeśli tworzenie tablicy nie jest krytyczne dla wydajności, uważam, że jest to dobry sposób na zainicjowanie tablicy w jednej linii

tjohns20
źródło
0

Nie jestem pewien, czy dobrze cię zrozumiałem. Rozumiem twoje pytanie w ten sposób: chcesz zainicjować wektor dla dużej liczby elementów. Co jest złego w używaniu push_back()na wektorze? :-)

Jeśli znasz liczbę elementów do przechowywania (lub jesteś pewien, że będzie przechowywać mniej niż następna potęga 2), możesz to zrobić, jeśli masz wektor wskaźników typu X (działa tylko ze wskaźnikami):

std::vector< X* > v;
v.reserve(num_elems);
X* p = v.begin();
for (int count = 0; count < num_elems; count++)
   p[count] = some_source[count];

Uważaj na dodanie więcej niż następnej mocy 2 elementów, nawet jeśli używasz push_back(). Wskaźniki do v.begin()będą wtedy nieważne.

mstrobl
źródło
Jest wiele problemów z tym kodem. Nie możesz po prostu wywołać vector :: Reserve (), a następnie zacząć manipulować wskaźnikami i zależeć od szczegółów implementacji magii, takich jak potęga dwóch. Iterator przekonwertowany na wskaźnik? A co z vector :: size ()? Na jakim kompilatorze i bibliotece STL to się w ogóle kompiluje?
styczeń
Zgadzam się, to bardzo zły kod. A co najlepsze, powinieneś zapomnieć o użyciu wskaźnika / iteratora i
cofnąć się
Nie, to całkowicie legalny kod C ++. STL gwarantuje, że to zadziała: wektor przydzieli kolejne miejsce na co najmniej 2 ^ n elementów. W końcu wektor 2 ^ n elementów może zostać ponownie przydzielony, co daje nową przestrzeń za wektorem :: begin (). Przepraszamy, może to być hacky, ale to standard. :-)
mstrobl
1
... jednak jestem głęboko nieprzekonany, że używanie ciągłej gwarancji pamięci do dodawania nowych wartości za pomocą wskaźnika jest standardem - na początek wektor nie ma szansy zaktualizować swojego rozmiaru, więc jestem prawie pewien, że wyniki tego kodu są albo niezdefiniowane, albo wektor o rozmiarze 0.
Steve Jessop,
1
Oczywiście możesz skorzystać z ciągłej przestrzeni w wektorze; norma to gwarantuje. Jednak wywołanie rezerwacji () nie wystarczy, aby to zrobić; musisz dodać takie elementy, że size () jest poprawne. Możesz zmienić kod tak, aby przypisać odpowiednią liczbę elementów ....
styczeń