wake-up-neo.com

Warum kann ich keine Referenzvektoren erstellen?

Wenn ich das mache:

std::vector<int> hello;

Alles funktioniert super Wenn ich jedoch stattdessen einen Vektor von Referenzen mache:

std::vector<int &> hello;

Ich bekomme schreckliche Fehler wie 

fehler C2528: 'Zeiger': Zeiger auf Verweis ist ungültig

Ich möchte eine Reihe von Referenzen auf Strukturen in einen Vektor setzen, damit ich mich nicht mit Zeigern beschäftigen muss. Warum wirft der Vektor darüber einen Wutanfall auf? Ist meine einzige Möglichkeit, stattdessen einen Vektor von Zeigern zu verwenden?

291
Colen

Der Komponententyp von Containern wie Vektoren muss zuweisbar sein. Referenzen können nicht zugewiesen werden (Sie können sie nur einmal initialisieren, wenn sie deklariert sind, und Sie können sie später nicht auf etwas anderes verweisen). Andere nicht zuweisbare Typen sind ebenfalls nicht als Komponenten von Containern zulässig, z. vector<const int> ist nicht erlaubt.

282
newacct

ja, du kannst nach std::reference_wrapper suchen, das eine Referenz nachahmt, aber zuweisbar ist und auch "reseating"

95
Ion Todirel

Referenzen können naturgemäß nur zum Zeitpunkt ihrer Erstellung gesetzt werden; die folgenden zwei Zeilen haben sehr unterschiedliche Wirkungen:

int & A = B;   // makes A an alias for B
A = C;         // assigns value of C to B.

Des weiteren ist das illegal:

int & D;       // must be set to a int variable.

Wenn Sie jedoch einen Vektor erstellen, können Sie den Elementen bei der Erstellung keine Werte zuweisen. Sie machen im Wesentlichen nur eine ganze Reihe des letzten Beispiels.

27
James Curran

Ion Todirel erwähnte bereits eine AntwortYESmit std::reference_wrapper. Seit C++ 11 haben wir einen Mechanismus, um ein Objekt aus std::vector abzurufen und die Referenz mit std::remove_reference zu entfernen. Unten ist ein Beispiel angegeben, das mit g++ und clang mit der Option kompiliert wurde
-std=c++11 und erfolgreich ausgeführt.

#include <iostream>
#include <vector>
#include<functional>

class MyClass {
public:
    void func() {
        std::cout << "I am func \n";
    }

    MyClass(int y) : x(y) {}

    int getval()
    {
        return x;
    }

private: 
        int x;
};

int main() {
    std::vector<std::reference_wrapper<MyClass>> vec;

    MyClass obj1(2);
    MyClass obj2(3);

    MyClass& obj_ref1 = std::ref(obj1);
    MyClass& obj_ref2 = obj2;

    vec.Push_back(obj_ref1);
    vec.Push_back(obj_ref2);

    for (auto obj3 : vec)
    {
        std::remove_reference<MyClass&>::type(obj3).func();      
        std::cout << std::remove_reference<MyClass&>::type(obj3).getval() << "\n";
    }             
}
22
Steephen

boost::ptr_vector<int> wird funktionieren.

Edit: war ein Vorschlag zur Verwendung von std::vector< boost::ref<int> >, der nicht funktioniert, da Sie keinen boost::ref standardmäßig erstellen können.

14
Drew Dormann

Es ist ein Fehler in der C++ - Sprache. Sie können die Adresse einer Referenz nicht verwenden, da dies zu einem Verweis auf die Adresse des Objekts führen würde, sodass Sie niemals einen Zeiger auf eine Referenz erhalten können. std::vector arbeitet mit Zeigern auf seine Elemente, so dass auf die gespeicherten Werte verwiesen werden muss. Sie müssen stattdessen Zeiger verwenden.

12
Adam Rosenfield

Wie bereits erwähnt, werden Sie wahrscheinlich stattdessen einen Zeigervektor verwenden.

Möglicherweise möchten Sie jedoch die Verwendung eines ptr_vector in Betracht ziehen!

2
Martin Cote

TL; DR

Verwenden Sie std::reference_wrapper wie folgt:

#include <functional>
#include <string>
#include <vector>
#include <iostream>

int main()
{
    std::string hello = "Hello, ";
    std::string world = "everyone!";
    typedef std::vector<std::reference_wrapper<std::string>> vec_t;
    vec_t vec = {hello, world};
    vec[1].get() = "world!";
    std::cout << hello << world << std::endl;
    return 0;
}

Demo

Lange Antwort

Wie Standard schlägt vor für einen Standardcontainer X, der Objekte des Typs T enthält, muss TErasable von X sein.

Erasable bedeutet, dass der folgende Ausdruck gut geformt ist:

allocator_traits<A>::destroy(m, p)

A ist der Zuordnungstyp des Containers, m ist die Zuordnungsinstanz und p ist ein Zeiger vom Typ *T. Siehe hier für die Definition von Erasable.

Standardmäßig wird std::allocator<T> als Allokator des Vektors verwendet. Bei der Standardzuweisung entspricht die Anforderung der Gültigkeit von p->~T() (Beachten Sie, dass T ein Referenztyp und p ein Zeiger auf eine Referenz ist). Jedoch Zeiger auf eine Referenz ist illegal , daher ist der Ausdruck nicht gut geformt.

1
ivaigult

Wie aus den anderen Kommentaren hervorgeht, sind Sie auf die Verwendung von Zeigern beschränkt. Wenn dies jedoch hilfreich ist, können Sie die direkte Verwendung von Zeigern vermeiden.

Sie können etwas wie das Folgende tun:

vector<int*> iarray;
int default_item = 0; // for handling out-of-range exception

int& get_item_as_ref(unsigned int idx) {
   // handling out-of-range exception
   if(idx >= iarray.size()) 
      return default_item;
   return reinterpret_cast<int&>(*iarray[idx]);
}
0
Omid