2009-05-28 8 views
249

Wenn ich dies tun:Warum kann ich keinen Vektor von Referenzen erstellen?

std::vector<int> hello; 

Alles funktioniert super. Allerdings, wenn ich es einen Vektor von Referenzen machen statt:

std::vector<int &> hello; 

ich schreckliche Fehler wie „Fehler C2528:‚Zeiger‘: Zeiger zu verweisen ist illegal“.

Ich möchte eine Reihe von Verweisen auf Strukturen in einen Vektor, so dass ich nicht mit Zeigern einmischen muss. Warum wirft Vektor einen Wutanfall darüber? Ist meine einzige Option, stattdessen einen Zeigervektor zu verwenden?

+34

Sie std verwenden können :: vector > hallo; Siehe http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=217 –

Antwort

11

Es ist ein Fehler in der C++ Sprache. Sie können nicht die Adresse einer Referenz nehmen, da der Versuch, dies zu tun, zur Folge hätte, dass die Adresse des Objekts, auf das Bezug genommen wird, erhalten wird und Sie somit niemals einen Zeiger auf eine Referenz erhalten können. std::vector arbeitet mit Zeigern auf seine Elemente, sodass auf die gespeicherten Werte verwiesen werden muss. Sie müssen stattdessen Zeiger verwenden.

+0

Ich denke, es könnte mit einem void * -Puffer und Placement neu implementiert werden. Nicht, dass das viel Sinn machen würde. – peterchen

+46

"Fehler in der Sprache" ist zu stark. Es ist von Entwurf. Ich denke nicht, dass es erforderlich ist, dass Vektor mit Zeigern auf Elemente arbeitet. Es ist jedoch erforderlich, dass das Element zuweisbar ist. Referenzen sind nicht. –

+0

Sie können die 'sizeof' auch nicht als Referenz verwenden. –

26

Aufgrund ihrer Natur können Referenzen nur zum Zeitpunkt ihrer Erstellung festgelegt werden; dh die beiden folgenden Zeilen haben sehr unterschiedliche Effekte:

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

Futher, das ist illegal:

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

Wenn Sie jedoch einen Vektor zu erstellen, gibt es keine Möglichkeit Werte zuweisen, um es Artikel ist bei Schaffung. Sie machen im Wesentlichen nur eine ganze Menge des letzten Beispiels.

+6

"Wenn Sie einen Vektor erstellen, gibt es keine Möglichkeit, den Elementen bei der Erstellung Werte zuzuweisen" Ich verstehe nicht, was Sie mit dieser Aussage meinen. Was sind "seine Gegenstände bei der Schöpfung"? Ich kann einen leeren Vektor erstellen. Und ich kann Elemente mit .push_back() hinzufügen. Sie weisen darauf hin, dass Verweise nicht standardmäßig konstruierbar sind. Aber ich kann definitiv Vektoren von Klassen haben, die nicht standardmäßig konstruierbar sind. – newacct

+1

Das Element ypte von std :: vector muss nicht standardmäßig konstruierbar sein. Sie können struct schreiben A {A (int); privat: A(); }; Vektor a; gerade gut - solange Sie nicht solche Methoden verwenden, die erfordern, dass sie standardmäßig konstruierbar sind (wie v.resize (100); - aber stattdessen müssen Sie v.resize tun (100, A (1));) –

+0

Und wie würdest du in diesem Fall eine solche push_back() schreiben? Es wird immer noch die Zuweisung und nicht die Konstruktion verwenden. –

250

Der Komponententyp von Containern wie Vektoren muss assignable sein. Verweise sind nicht zuweisbar (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 Behältern, z. vector<const int> ist nicht erlaubt.

+1

Willst du sagen, ich kann keinen Vektorenvektor haben? (Ich bin sicher, dass ich das getan habe ...) –

+6

Ja, ein std :: vector > ist korrekt, std :: vector ist zuweisbar. –

+14

Tatsächlich ist dies der "tatsächliche" Grund. Der Fehler, dass T * unmöglich ist, ist U und ist nur ein Nebeneffekt der verletzten Anforderung, dass T zuweisbar sein muss. Wenn der Vektor den Typparameter genau überprüfen könnte, würde er wahrscheinlich "verletzte Anforderung: T & nicht zuweisbar" sagen. –

14

boost::ptr_vector<int> wird funktionieren.

Edit: war ein Vorschlag std::vector< boost::ref<int> > zu verwenden, das wird nicht funktionieren, weil Sie keine boost::ref Standard-Konstrukt können.

+7

Aber Sie können einen Vektor oder nicht standardmäßig konstruierbar Typen haben, oder? Sie müssen nur darauf achten, nicht den Standard-Ctor zu verwenden. des Vektors – Manuel

+1

@Manuel: Oder 'resize'. –

+5

Seien Sie vorsichtig, Boosts Pointer-Container übernehmen ausschließlichen Besitz der Pointees. [Zitat] (http://www.boost.org/doc/libs/1_57_0/libs/ptr_container/doc/ptr_container.html # motivation): "Wenn Sie eine gemeinsame Semantik benötigen, ist diese Bibliothek nicht das, was Sie brauchen." –

2

Wie andere erwähnt haben, werden Sie wahrscheinlich am Ende einen Zeigervektor verwenden.

Sie können jedoch stattdessen eine ptr_vector in Betracht ziehen! ja

+3

Diese Antwort ist nicht praktikabel, da ein ptr_vector ein Speicher sein soll. Das wird die Zeiger beim Entfernen löschen. So ist es nicht für seinen Zweck verwendbar. –

74

Sie können, suchen Sie nach std::reference_wrapper, dass ahmt ein Referenz aber belegbar ist und auch „eingeschliffen“ werden kann

+1

Gibt es eine Möglichkeit, "get()' zuerst aufzurufen, wenn man versucht, auf eine Methode einer Instanz einer Klasse in diesem Wrapper zuzugreifen? Z.B. 'reference_wrapper my_ref (...); my_ref.get(). doStuff(); 'ist nicht sehr Referenz wie. – timdiels

+3

Ist es nicht unausweichlich, den Typ selbst durch Zurückgeben der Referenz zu gießen? – WorldSEnder

16

Ion Todirel erwähnt bereits eine Antwort JAstd::reference_wrapper verwenden. Seit C++ 11 haben wir einen Mechanismus zum Abrufen von Objekt aus std::vector und entfernen Sie die Referenz mit std::remove_reference. Im Folgenden wird ein Beispiel aufgeführt, das unter Verwendung von g++ und clang mit der Option
-std=c++11 erstellt und erfolgreich ausgeführt wurde.

#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"; 
    }    
} 
+5

Ich sehe den Wert in 'std :: remove_reference <>' hier nicht. Der Punkt von 'std :: remove_reference <>' ist, Ihnen zu erlauben, "den Typ T zu schreiben, aber ohne eine Referenz zu sein, wenn es eins ist". So ist 'std :: remove_reference :: type' genau das selbe wie' MyClass' zu schreiben. – alastair

+0

Kein Wert überhaupt - Sie können einfach schreiben ** 'für (MyClass obj3: vec) std :: cout << obj3.getval() <<" \ n ";' ** (oder 'für (const MyClass & obj3: vec) 'wenn Sie' getval() 'const deklarieren, wie Sie sollten). –

0

Wie die anderen Kommentare vorschlagen, sind Sie auf die Verwendung von Zeigern beschränkt. Aber wenn es hilft, hier ist eine Technik, um direkt mit Zeigern zu vermeiden.

Sie können so etwas wie folgt vor:

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

'reinterpret_cast' wird nicht benötigt – Xeverous

Verwandte Themen