Initialisierung von 'const std :: vector <T>' wie ein AC-Array


79

Gibt es eine elegante Möglichkeit, ein const std::vector<const T>Like const T a[] = { ... }für eine feste (und kleine) Anzahl von Werten zu erstellen und zu initialisieren ?
Ich muss häufig eine Funktion aufrufen, die a erwartet vector<T>, aber diese Werte werden sich in meinem Fall nie ändern.

Im Prinzip dachte ich an so etwas

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

da v außerhalb dieser Kompilierungseinheit nicht verwendet wird.

Antworten:


61

Sie müssen entweder auf C ++ 0x warten oder etwas wie Boost.Assign verwenden, um dies zu tun.

z.B:

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

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

für C ++ 11:

vector<int> luggage_combo = { 1, 2, 3, 4, 5 };

1
"Muss" könnte etwas stark sein; Der zweistufige Ansatz kann auch funktionieren. Obwohl der Boost-Ansatz sicherlich gut ist.
Januar

Geussing du meintest: #include <boost / assign / std / vector.hpp>
Zac

1
@ Jason: boost.assign definiert Vorlagenüberladungen von Operator + = und Operator auf Vektor <T>
Ferruccio

aber wie fängt man jeden der Kommawerte ab?
Jason S

41
12345? Ich habe die gleiche Kombination auf meinem Gepäck!
JRG

39

Wenn Sie fragen, wie ein const-Vektor so initialisiert werden soll, dass er interessante Inhalte enthält, besteht die Antwort wahrscheinlich darin, den Kopierkonstruktor zu verwenden. Zuerst füllen Sie mühsam einen Vektor aus, dann erstellen Sie daraus Ihren neuen const-Vektor. Oder Sie können die vector<InputIterator>(InputIterator, InputIterator)Konstruktorvorlage verwenden, um aus einem anderen Container oder Array zu initialisieren. Wenn es sich um ein Array handelt, könnte dies mit einer Initialisierungsliste definiert worden sein.

So etwas ist hoffentlich nah an dem, was Sie wollen:

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

Wenn Sie fragen, wie ein konstanter Vektor an eine Funktion übergeben werden soll, die einen Vektor akzeptiert, lautet die Antwort entweder:

  • Sie können nicht, weil die Funktion den Vektor ändern könnte und Ihr Objekt / Ihre Referenz const ist. Erstellen Sie eine nicht konstante Kopie des Originals und geben Sie diese weiter.

oder

  • Verwenden Sie const_castdiese Option, um die Konstanz zu entfernen und sie an eine Funktion zu übergeben, die einen Nicht-Konstanten-Vektor verwendet, von der Sie jedoch zufällig wissen, dass sie den Vektor nicht ändert.

Letzteres ist eines der Dinge, die zu Recht dazu führen, dass jeder, der es sieht, Kommentare zu Schutzbrillen abgibt und dass sie nichts tun. Es ist genau das, wofür es const_castist, aber es gibt ein ziemlich starkes Argument, das besagt, dass const_castSie bereits verloren haben , wenn Sie es brauchen .

Beides zu tun (mit dem Kopierkonstruktor einen konstanten Vektor aus einem nicht konstanten Vektor zu erstellen und dann die Konstanz wegzuwerfen) ist definitiv falsch - Sie sollten nur einen nicht konstanten Vektor verwenden. Wählen Sie also höchstens eines davon aus, um es zu tun ...

[ Bearbeiten: habe gerade bemerkt, dass Sie über einen Unterschied zwischen vector<T>und sprechen const vector<const T>. Leider in der STL, vector<const T>und vector<T>sind völlig unabhängige Typen, und die einzige Möglichkeit, zwischen ihnen zu konvertieren, ist durch Kopieren. Dies ist ein Unterschied zwischen Vektoren und Arrays - a T**kann lautlos und sicher in const T *const *] konvertiert werden


Wenn ich t1, t2, t3 nicht kopieren möchte, wie kann ich das am besten tun? Dies ist die einzige / beste Lösung, die ich bisher gefunden habe: <code> const int * p = & ra [0]; Vektor <const int *> v; für (int i = 0; i <Größe; ++ i, ++ p) v.push_back (p); </ code>
AudioDroid

@AudioDroid: Ja, es gibt keine Möglichkeit, etwas in einen Vektor einzufügen, ohne ihn zu kopieren (oder in C ++ 11 zu verschieben). Der nächste Wert ist also ein Vektor von Zeigern (oder intelligenten Zeigern). Wenn sich Ihre drei Objekte jedoch bereits in einem Array befinden, macht es normalerweise keinen Sinn, einen Vektor mit Zeigern darauf zu erstellen: Verwenden Sie das Array einfach für das, wofür Sie den Vektor verwenden würden.
Steve Jessop

15

Kurzer und schmutziger Weg (ähnlich wie bei 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"));
}

Jetzt verfügt C ++ 11 über Initialisierungslisten, sodass Sie dies nicht auf diese Weise tun oder sogar Boost verwenden müssen. Als Beispiel können Sie Folgendes in C ++ 11 effizienter ausführen:

    #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;
        }
    }

Es ist jedoch immer noch nicht so effizient wie die Verwendung einer C ++ 11-Initialisierungsliste, da kein operator=(vlist_of&&)Vektor definiert ist.

Die Art und Weise, wie tjohns20 wie folgt modifiziert wurde, könnte ein besseres c ++ 11 sein 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;
    }
    
}

böse, aber nützlich zum Testen usw. ++
Eli Bendersky

12

Wie andere bereits gesagt haben, können Sie einen Vektor nicht auf die gleiche Weise wie ein Array im C-Stil initiieren, es sei denn, Sie geben ihm Zeiger auf ein Quellarray. Wenn Ihr Vektor in diesem Fall eine globale Konstante ist, verwenden Sie stattdessen einfach ein altes Array im C-Stil.

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

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

Sie können sogar STL-Algorithmen für dieses Array verwenden, genauso wie Sie Algorithmen für einen konstanten Vektor verwenden würden ...

const int* myInt = std::find( &MyInts[0], &MyInts[NumMyInts], 3);

4
Das ist ein guter Punkt, aber er übergibt ihn an eine Funktion, die einen Vektor erwartet. Möglicherweise kann er diese Funktion nicht ändern.
Ferruccio

Dies erfordert die externe Verfolgung der Größe des Vektors, was beim Umgang mit vielen Vektoren schmerzhaft ist. Andernfalls wäre diese Lösung die beste, da nur eine Kopie der Ganzzahlen im Speicher erforderlich ist.
MasterHD

6

Sie können dies in zwei Schritten tun:

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])));
}

Vielleicht nicht so schön wie Sie möchten, aber funktional.


5

Wie wäre es mit:

int ar[]={1,2,3,4,5,6};
const int TotalItems = sizeof(ar)/sizeof(ar[0]);
std::vector<int> v(ar, ar+TotalItems);

Egal, "Steve Jessop" hat diesen Ansatz bereits hervorgehoben.
Opal

3

Alte Frage, aber ich bin heute auf dasselbe Problem gestoßen. Hier ist der Ansatz, der für meine Zwecke am akzeptabelsten war:

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;
}

Beispiel zur Vermeidung übermäßigen Kopierens:

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;
}

0

Wenn sie alle gleich sind, können Sie es einfach tun

vector<T> vec(num_items, item);

aber ich nehme an, dass dies nicht der Fall ist - in diesem Fall ist der wahrscheinlich ordentlichste Weg:

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

Mit C ++ 0x können Sie eine Initialisierungsliste genau so verwenden, wie Sie es sich vorstellen, aber das ist momentan leider nicht sehr gut.


0

Basierend auf der Antwort von Shadow2531 verwende ich diese Klasse, um Vektoren zu initialisieren, ohne tatsächlich von std :: vector zu erben, wie es die Lösung von Shadow getan hat

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;
};

Verwendung:

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

Im Vergleich zu Steve Jessops Lösung wird viel mehr Code erstellt, aber wenn die Array-Erstellung nicht leistungskritisch ist, finde ich es eine gute Möglichkeit, ein Array in einer einzelnen Zeile zu initialisieren


0

Ich bin mir nicht sicher, ob ich dich richtig verstanden habe. Ich verstehe Ihre Frage so: Sie möchten einen Vektor mit einer großen Anzahl von Elementen initialisieren. Was ist falsch an der Verwendung push_back()des Vektors? :-)

Wenn Sie die Anzahl der zu speichernden Elemente kennen (oder sicher sind, dass weniger als die nächste Potenz von 2 gespeichert wird), können Sie dies tun, wenn Sie einen Vektor von Zeigern vom Typ X haben (funktioniert nur mit Zeigern):

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];

Achten Sie darauf, dass Sie nicht mehr als die nächste Potenz von 2 Elementen hinzufügen, auch wenn Sie diese verwenden push_back(). Zeiger auf v.begin()sind dann ungültig.


Es gibt viele Probleme mit diesem Code. Sie können nicht einfach vector :: Reserve () aufrufen und dann mit der Manipulation von Zeigern und abhängig von Details der magischen Implementierung wie Zweierpotenzen beginnen. Iterator in Zeiger umgewandelt? Was ist mit vector :: size ()? Auf welchem ​​Compiler und welcher STL-Bibliothek wird dies überhaupt kompiliert?
Januar

Ich bin damit einverstanden, dass dies ein sehr schlechter Code ist. A das Beste, sollten Sie die Verwendung des Zeigers / Iterators und Fallback auf push_back
paercebal

Nein, das ist vollkommen legaler C ++ - Code. Die STL garantiert, dass dies funktioniert: Der Vektor weist mindestens 2 ^ n Elementen aufeinanderfolgenden Platz zu. Nachdem alle 2 ^ n Elemente Vektor neu zugeordnet werden dürfen, erhalten Sie einen neuen Platz hinter vector :: begin (). Entschuldigung, es mag hackig sein, aber es ist der Standard. :-)
mstrobl

1
... Ich bin jedoch zutiefst nicht davon überzeugt, dass die Verwendung der Garantie für zusammenhängenden Speicher zum Hinzufügen neuer Werte per Zeiger Standard ist. Für den Anfang hat der Vektor keine Chance, seine eigene Größe zu aktualisieren, daher bin ich mir ziemlich sicher, dass die Ergebnisse vorliegen von diesem Code sind entweder undefiniert oder ein Vektor der Größe 0.
Steve Jessop

1
Natürlich können Sie den zusammenhängenden Raum im Vektor nutzen. Der Standard garantiert dies. Der Aufruf von Reserve () reicht dazu jedoch nicht aus; Sie müssen die Elemente so hinzufügen, dass size () korrekt ist. Sie können den Code so ändern, dass Sie die richtige Anzahl von Elementen zugewiesen haben ....
Januar 2008
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.