Pasando referencias a punteros en C ++

130

Por lo que puedo decir, no hay razón para que no se me permita pasar una referencia a un puntero en C ++. Sin embargo, mis intentos de hacerlo están fallando, y no tengo idea de por qué.

Esto es lo que estoy haciendo:

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

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

Y obtengo este error:

no se puede convertir el parámetro 1 de 'std :: string *' a 'std :: string * &'

Alex
fuente

Respuestas:

126

Su función espera una referencia a un puntero de cadena real en el alcance de la llamada, no un puntero de cadena anónimo. Así:

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

debería compilar bien.

Sin embargo, esto solo es útil si tiene la intención de modificar el puntero que pasa a la función. Si tiene la intención de modificar la cadena en sí misma, debe usar una referencia a la cadena como sugirió Sake. Con eso en mente, debería ser más obvio por qué el compilador se queja de su código original. En su código, el puntero se crea 'sobre la marcha', modificando ese puntero no tendría ninguna consecuencia y eso no es lo que se pretende. La idea de una referencia (frente a un puntero) es que una referencia siempre apunta a un objeto real.

Chris
fuente
86

El problema es que está intentando vincular un temporal a la referencia, que C ++ no permite a menos que la referencia lo sea const.

Entonces puede hacer uno de los siguientes:

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
fuente
Es especialmente instructivo para escribir el tipo de la manera Burr hace en el ejemplo N ° 2 - string* const& valen lugar de un equivalente de menos legibles, como por ejemplo const string* &val. A mis ojos, esto es claramente una referencia constante , a un puntero, a una cadena. Personalmente prefiero escribir en T const&lugar de const T&declaraciones para obtener esta aclaración exacta.
fish2000
1
Nit pick (no es una crítica): para mí, la frase bind a temporary to the referencees más clara en esta respuesta que @Chris 'as reference to an actual string pointer in the calling scope, not an anonymous string pointer. Independientemente, ambos son probablemente correctos desde mi perspectiva.
kevinarpe
1
@ fish2000 No solo eso, const string*&y en string* const&realidad son diferentes tipos. El primero es una no constreferencia a a const string*, mientras que el segundo es una constreferencia a a string*.
Justin Time - Restablece a Monica el
@ fish2000 tenga cuidado de "preferir" T const&a const T&- como señala @Justin Time, son diferentes tipos. Es posible que no tienen consecuencias a veces, pero en otras situaciones será absolutamente crítico para preferir uno u otro, al igual que prefieren inta double.
omatai
3
@ fish2000: uno dice "aquí hay una referencia que puedes cambiar a algo que no puedes cambiar" mientras que el otro dice "aquí hay una referencia que no puedes cambiar a algo que puedes cambiar". En este ejemplo dado, simplemente no puedes intercambiar los dos.
omatai
10

Cámbielo a:

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

EDITAR:

Esto se llama ref-to-pointery no puede pasar una dirección temporal como referencia para funcionar. (a menos que lo sea const reference)

Sin embargo, he mostrado std::string* pS = &s;(puntero a una variable local), su uso típico sería: cuando desea que la persona que llama cambie el puntero en sí, no el objeto al que apunta. Por ejemplo, una función que asigna memoria y asigna la dirección del bloque de memoria que asignó a su argumento debe tomar una referencia a un puntero, o un puntero a puntero:

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

}
aJ.
fuente
5

&s produce un puntero temporal a una cadena y no puede hacer referencia a un objeto temporal.

n0rd
fuente
44
Esto no es del todo cierto - puede hacer una referencia a const temporal
1800 INFORMACIÓN
3

Tratar:

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

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

o

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

// sometime later 
{
    // ...
    string s;
    myfunc(&s);
    // ...
}
Motivo
fuente
Sin embargo, para lo que estoy haciendo, necesito la dirección del puntero, así como el puntero en sí. No quiero pasar el puntero por valor.
Alex
Aún no entiendo lo que intentas lograr. No puede tener la "dirección del puntero" de "cadena s", simplemente porque "cadena s" no es un puntero.
Sake
@ Alex: supongo que debe detectar si la cadena es exactamente la misma que otra que está almacenando y no solo si su contenido es el mismo. Si ese es el caso, tenga en cuenta que puede usar la dirección del operador de una referencia y obtendrá la dirección del objeto referenciado: void f (std :: string const & s) {std :: string const * p = & s; }
David Rodríguez - dribeas 05 de
3

EDITAR: Experimenté un poco, y descubrí que las cosas son un poco más sutiles de lo que pensaba. Esto es lo que ahora creo que es una respuesta precisa.

&sno es un valor de l, por lo que no puede crear una referencia a menos que el tipo de referencia haga referencia a él const. Entonces, por ejemplo, no puedes hacer

string * &r = &s;

pero puedes hacer

string * const &r = &s;

Si coloca una declaración similar en el encabezado de la función, funcionará.

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

Hay otro problema, a saber, los temporales. La regla es que puede obtener una referencia a un temporal solo si es así const. Entonces, en este caso, se podría argumentar que & s es temporal, por lo que debe declararse consten el prototipo de la función. Desde un punto de vista práctico, no hay diferencia en este caso. (Es un valor o un valor temporal. De cualquier manera, se aplica la misma regla). Sin embargo, estrictamente hablando, creo que no es un valor temporal sino un valor. Me pregunto si hay una manera de distinguir entre los dos. (Quizás se define simplemente que todos los temporales son valores, y todos los que no son valores son temporales. No soy un experto en el estándar).

Dicho esto, su problema probablemente esté en un nivel superior. ¿Por qué quieres una referencia a la dirección de s? Si desea una referencia a un puntero s, debe definir un puntero como en

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

Si desea una referencia so un puntero s, haga lo más sencillo.

Ari
fuente
1

Acabo de hacer uso de una referencia a un puntero para hacer que todos los punteros en un árbol binario eliminado, excepto la raíz, sean seguros. Para hacer que el puntero sea seguro, solo tenemos que establecerlo en 0. No pude hacer que la función que elimina el árbol (manteniendo solo la raíz) acepte una referencia a un puntero ya que estoy usando la raíz (este puntero) como primer entrada para recorrer izquierda y derecha.

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

En pocas palabras, una referencia a un puntero no es válida cuando el parámetro formal es el (este) puntero.

Mohd
fuente
1

Bienvenido a C ++ 11 y referencias de valor:

#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);
    // ...
}

Ahora tiene acceso al valor del puntero (al que se refiere val), que es la dirección de la cadena.

Puede modificar el puntero y a nadie le importará. Ese es un aspecto de lo que es un valor en primer lugar.

Tenga cuidado: el valor del puntero solo es válido hasta que myfunc()regrese. Por fin, es temporal.

no-un-usuario
fuente
-1

Sé que es posible pasar referencias de punteros, lo hice la semana pasada, pero no recuerdo cuál era la sintaxis, ya que su código parece correcto para mi cerebro en este momento. Sin embargo, otra opción es utilizar punteros de punteros:

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

myfunc ("string * & val") esto en sí mismo no tiene ningún sentido. "string * & val" implica "string val", * y & se cancelan entre sí. Finalmente, uno no puede pasar la variable de cadena a una función ("string val"). Solo los tipos de datos básicos se pueden pasar a una función, ya que otros tipos de datos deben pasar como puntero o referencia. Puede tener string & val o string * val para una función.

Shashikiran
fuente
44
Mal en todos los niveles. Actualice la sintaxis de su declaración var.
Ari