Verweise auf Zeiger in C ++ übergeben

130

Soweit ich das beurteilen kann, gibt es keinen Grund, warum ich keinen Verweis auf einen Zeiger in C ++ übergeben darf. Meine Versuche scheitern jedoch, und ich habe keine Ahnung warum.

Das mache ich:

void myfunc(string*& val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(&s);
    // ...
}

Und ich bekomme diesen Fehler:

Parameter 1 kann nicht von 'std :: string *' in 'std :: string * &' konvertiert werden.

Alex
quelle

Antworten:

126

Ihre Funktion erwartet einen Verweis auf einen tatsächlichen Zeichenfolgenzeiger im aufrufenden Bereich, nicht auf einen anonymen Zeichenfolgenzeiger. So:

string s;
string* _s = &s;
myfunc(_s);

sollte gut kompilieren.

Dies ist jedoch nur nützlich, wenn Sie den Zeiger ändern möchten, den Sie an die Funktion übergeben. Wenn Sie die Zeichenfolge selbst ändern möchten, sollten Sie einen Verweis auf die Zeichenfolge verwenden, wie von Sake vorgeschlagen. In diesem Sinne sollte es offensichtlicher sein, warum sich der Compiler über Ihren ursprünglichen Code beschwert. In Ihrem Code wird der Zeiger "on the fly" erstellt. Das Ändern dieses Zeigers hätte keine Konsequenzen, und das ist nicht beabsichtigt. Die Idee einer Referenz (gegenüber einem Zeiger) ist, dass eine Referenz immer auf ein tatsächliches Objekt zeigt.

Chris
quelle
86

Das Problem ist, dass Sie versuchen, ein temporäres Element an die Referenz zu binden, was C ++ nur zulässt, wenn die Referenz dies ist const .

Sie können also eine der folgenden Aktionen ausführen:

void myfunc(string*& val)
{
    // Do stuff to the string pointer
}


void myfunc2(string* const& val)
{
    // Do stuff to the string pointer
}

int main()
// sometime later 
{
    // ...
    string s;
    string* ps = &s;

    myfunc( ps);   // OK because ps is not a temporary
    myfunc2( &s);  // OK because the parameter is a const&
    // ...

    return 0;
}
Michael Burr
quelle
Es ist besonders lehrreich, den Typ so aufzuschreiben, wie es Mr. Burr in Beispiel Nr. 2 tut - string* const& valanstelle eines weniger lesbaren Äquivalents wie z const string* &val. Für meine Augen ist dies eindeutig eine konstante Referenz , auf einen Zeiger, auf eine Zeichenfolge. Ich persönlich schreibe T const&lieber als const T&in Erklärungen, um genau diese Klarstellung zu erhalten.
fish2000
1
Nit pick (keine Kritik): Für mich ist der Satz bind a temporary to the referencein dieser Antwort klarer als bei @Chris 'as reference to an actual string pointer in the calling scope, not an anonymous string pointer. Unabhängig davon sind beide aus meiner Sicht wahrscheinlich richtig.
Kevinarpe
1
@ fish2000 Nicht nur das, const string*&und string* const&sind eigentlich verschiedene Typen. Die erste ist eine Nichtreferenz constauf a const string*, während die zweite eine constReferenz auf a ist string*.
Justin Time - Stellen Sie Monica
@ fish2000 passen sie von „Bevorzugung des “ T const&zu const T&- wie @Justin Zeit weist darauf hin, sie unterschiedliche Typen sind. Es kann keine Folgen haben manchmal, aber in anderen Situationen wird es absolut entscheidend sein , einen oder anderen zu bevorzugen, ähnlich wie lieber intzu double.
Omatai
3
@ fish2000 - einer sagt "hier ist eine Referenz, die Sie in etwas ändern können, das Sie nicht ändern können", während der andere sagt "hier ist eine Referenz, die Sie nicht in etwas ändern können, das Sie ändern können". In diesem Beispiel können Sie die beiden einfach nicht austauschen.
Omatai
10

Ändern Sie es in:

  std::string s;
  std::string* pS = &s;
  myfunc(pS);

BEARBEITEN:

Dies wird aufgerufen ref-to-pointerund Sie können keine temporäre Adresse als Referenz auf die Funktion übergeben. (es sei denn, es istconst reference ).

Obwohl ich gezeigt habe std::string* pS = &s;(Zeiger auf eine lokale Variable), wäre seine typische Verwendung: Wenn Sie möchten, dass der Angerufene den Zeiger selbst ändert, nicht das Objekt, auf das er zeigt. Beispielsweise muss eine Funktion, die Speicher zuweist und die Adresse des Speicherblocks zuweist, den sie ihrem Argument zugewiesen hat, einen Verweis auf einen Zeiger oder einen Zeiger auf einen Zeiger enthalten:

void myfunc(string*& val)
{
//val is valid even after function call
   val = new std::string("Test");

}
aJ.
quelle
5

&s Erzeugt einen temporären Zeiger auf eine Zeichenfolge und Sie können nicht auf ein temporäres Objekt verweisen.

n0rd
quelle
4
Dies ist nicht ganz richtig - Sie können einen Verweis auf const temporär machen
1800 INFORMATION
3

Versuchen:

void myfunc(string& val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(s);
    // ...
}

oder

void myfunc(string* val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(&s);
    // ...
}
Sake
quelle
Für das, was ich mache, brauche ich die Adresse des Zeigers sowie den Zeiger selbst. Ich möchte den Zeiger nicht als Wert übergeben.
Alex
Verstehe immer noch nicht, was du erreichen willst. Sie können nicht "Adresse des Zeigers" von "Zeichenfolge s" haben, einfach weil "Zeichenfolge s" kein Zeiger ist.
Sake
@Alex: Ich gehe davon aus, dass Sie feststellen müssen, ob die Zeichenfolge genau mit einer anderen Zeichenfolge übereinstimmt, die Sie speichern, und nicht nur, ob deren Inhalt identisch ist. Wenn dies der Fall ist, beachten Sie, dass Sie den Operator address-of für eine Referenz verwenden können und Sie die Adresse des referenzierten Objekts erhalten: void f (std :: string const & s) {std :: string const * p = & s; }
David Rodríguez - Dribeas
3

EDIT: Ich habe einige experimentiert und festgestellt, dass die Dinge etwas subtiler sind, als ich dachte. Ich denke jetzt, dass dies eine genaue Antwort ist.

&sist kein l-Wert, daher können Sie keinen Verweis darauf erstellen, es sei denn, der Typ des Verweises ist ein Verweis darauf const. So können Sie zum Beispiel nicht tun

string * &r = &s;

aber du kannst es tun

string * const &r = &s;

Wenn Sie eine ähnliche Deklaration in den Funktionsheader einfügen, funktioniert dies.

void myfunc(string * const &a) { ... }

Es gibt noch ein anderes Problem, nämlich Provisorien. Die Regel ist, dass Sie nur dann einen Verweis auf eine temporäre Datei erhalten können, wenn dies der Fall ist const. In diesem Fall könnte man also argumentieren, dass & s vorübergehend ist und daher deklariert werden mussconst im Funktionsprototyp . Aus praktischer Sicht macht es in diesem Fall keinen Unterschied. (Es ist entweder ein r-Wert oder ein temporärer Wert. In beiden Fällen gilt die gleiche Regel.) Genau genommen denke ich jedoch, dass es kein temporärer, sondern ein r-Wert ist. Ich frage mich, ob es einen Weg gibt, zwischen den beiden zu unterscheiden. (Vielleicht wird einfach definiert, dass alle Provisorien r-Werte sind und alle Nicht-l-Werte temporäre Werte. Ich bin kein Experte für den Standard.)

Davon abgesehen liegt Ihr Problem wahrscheinlich auf einem höheren Niveau. Warum möchten Sie einen Verweis auf die Adresse von s? Wenn Sie einen Verweis auf einen Zeiger auf möchten s, müssen Sie einen Zeiger wie in definieren

string *p = &s;
myfunc(p);

Wenn Sie einen Verweis auf soder einen Zeiger auf möchten s, gehen Sie einfach vor.

Ari
quelle
1

Ich habe gerade einen Verweis auf einen Zeiger verwendet, um alle Zeiger in einem gelöschten Binärbaum außer dem Stamm sicher zu machen. Um den Zeiger sicher zu machen, müssen wir ihn nur auf 0 setzen. Ich konnte die Funktion, die den Baum löscht (nur die Wurzel behalten), nicht dazu bringen, einen Verweis auf einen Zeiger zu akzeptieren, da ich die Wurzel (diesen Zeiger) als erste verwende Eingabe, um links und rechts zu durchlaufen.

void BinTree::safe_tree(BinTree * &vertex ) {
    if ( vertex!=0 ) {  // base case
        safe_tree(vertex->left);    // left subtree.
            safe_tree(vertex->right);   //  right subtree.
          // delete vertex;  // using this delete causes an error, since they were deleted on the fly using inorder_LVR. If inorder_LVR does not perform delete to the nodes, then, use delete vertex;
        vertex=0;  // making a safe pointer
    }
} // end in

Unterm Strich ist ein Verweis auf einen Zeiger ungültig, wenn der formale Parameter der (dieser) Zeiger ist.

Mohd
quelle
1

Willkommen bei C ++ 11 und rvalue Referenzen:

#include <cassert>
#include <string>

using std::string;

void myfunc(string*&& val)
{
    assert(&val);
    assert(val);
    assert(val->c_str());
    // Do stuff to the string pointer
}

// sometime later 
int main () {
    // ...
    string s;
    myfunc(&s);
    // ...
}

Jetzt haben Sie Zugriff auf den Wert des Zeigers (auf den verwiesen wird val), der die Adresse der Zeichenfolge ist.

Sie können den Zeiger ändern, und es interessiert niemanden. Das ist ein Aspekt dessen, was ein Wert überhaupt ist.

Achtung: Der Wert des Zeigers ist nur bis zur myfunc()Rückkehr gültig . Endlich ist es eine vorübergehende.

kein Benutzer
quelle
-1

Ich weiß, dass es möglich ist, Verweise auf Zeiger zu übergeben, ich habe es letzte Woche getan, aber ich kann mich nicht erinnern, wie die Syntax lautete, da Ihr Code für mein Gehirn im Moment korrekt aussieht. Eine andere Option ist jedoch die Verwendung von Zeigern von Zeigern:

Myfunc(String** s)
Robert Gould
quelle
-8

myfunc ("string * & val") das selbst macht keinen Sinn. "string * & val" impliziert "string val", * und & heben sich gegenseitig auf. Schließlich kann man keine Stringvariable an eine Funktion übergeben ("string val"). Es können nur grundlegende Datentypen an eine Funktion übergeben werden, da andere Datentypen als Zeiger oder Referenz übergeben werden müssen. Sie können entweder string & val oder string * val für eine Funktion haben.

Shashikiran
quelle
4
Auf allen Ebenen falsch. Bitte aktualisieren Sie Ihre var-Deklarationssyntax.
Ari