Passer des références à des pointeurs en C ++

130

Pour autant que je sache, il n'y a aucune raison pour laquelle je ne devrais pas être autorisé à passer une référence à un pointeur en C ++. Cependant, mes tentatives pour le faire échouent, et je n'ai aucune idée pourquoi.

Voici ce que je fais:

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

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

Et j'obtiens cette erreur:

impossible de convertir le paramètre 1 de 'std :: string *' en 'std :: string * &'

Alex
la source

Réponses:

126

Votre fonction attend une référence à un pointeur de chaîne réel dans la portée appelante, pas à un pointeur de chaîne anonyme. Donc:

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

devrait compiler très bien.

Cependant, cela n'est utile que si vous avez l'intention de modifier le pointeur que vous passez à la fonction. Si vous avez l'intention de modifier la chaîne elle-même, vous devez utiliser une référence à la chaîne comme Sake l'a suggéré. Dans cet esprit, il devrait être plus évident pourquoi le compilateur se plaint de votre code d'origine. Dans votre code, le pointeur est créé «à la volée», modifier ce pointeur n'aurait aucune conséquence et ce n'est pas ce qui est prévu. L'idée d'une référence (par rapport à un pointeur) est qu'une référence pointe toujours vers un objet réel.

Chris
la source
86

Le problème est que vous essayez de lier un temporaire à la référence, ce que C ++ n'autorise pas à moins que la référence ne le soit const.

Vous pouvez donc effectuer l'une des opérations suivantes:

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
la source
Il est particulièrement instructif d'écrire le type comme le fait M. Burr dans l'exemple n ° 2 - string* const& valau lieu d'un équivalent moins lisible comme par exemple const string* &val. À mes yeux, c'est clairement une référence const , à un pointeur, à une chaîne. Personnellement, je préfère écrire T const&plutôt que const T&dans les déclarations pour obtenir cette précision exacte.
fish2000 le
1
Nit pick (pas une critique): Pour moi, la phrase bind a temporary to the referenceest plus claire dans cette réponse que @Chris 'as reference to an actual string pointer in the calling scope, not an anonymous string pointer. Quoi qu'il en soit, les deux sont probablement corrects de mon point de vue.
kevinarpe
1
@ fish2000 Non seulement cela, const string*&et string* const&sont en fait des types différents. Le premier est une non- constréférence à a const string*, tandis que le second est une constréférence à a string*.
Justin Time - Réintègre Monica
@ fish2000 méfiez-vous de "préférer" T const&à const T&- comme le souligne @Justin Time, ce sont des types différents. Il peut avoir aucune conséquence parfois, mais dans d' autres situations , il sera absolument essentiel de préférer un ou l' autre, un peu comme préférant intà double.
omatai
3
@ fish2000 - l'un dit "voici une référence que vous pouvez changer en quelque chose que vous ne pouvez pas changer" tandis que l'autre dit "voici une référence que vous ne pouvez pas changer en quelque chose que vous pouvez changer". Dans cet exemple donné, vous ne pouvez tout simplement pas intervertir les deux.
omatai
10

Changez-le en:

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

ÉDITER:

Ceci est appelé ref-to-pointeret vous ne pouvez pas passer d'adresse temporaire comme référence à la fonction. (sauf si c'est le cas const reference).

Cependant, j'ai montré std::string* pS = &s;(pointeur vers une variable locale), son utilisation typique serait: lorsque vous voulez que l'appelé change le pointeur lui-même, pas l'objet vers lequel il pointe. Par exemple, une fonction qui alloue de la mémoire et attribue l'adresse du bloc de mémoire qu'elle a alloué à son argument doit prendre une référence à un pointeur, ou un pointeur à un pointeur:

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

}
un J.
la source
5

&s produit un pointeur temporaire vers une chaîne et vous ne pouvez pas faire référence à un objet temporaire.

n0rd
la source
4
Ce n'est pas tout à fait vrai - vous pouvez faire une référence à const temporaire
1800 INFORMATION
3

Essayer:

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

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

ou

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

// sometime later 
{
    // ...
    string s;
    myfunc(&s);
    // ...
}
Saké
la source
Pour ce que je fais cependant, j'ai besoin de l'adresse du pointeur ainsi que du pointeur lui-même. Je ne veux pas passer le pointeur par valeur.
Alex
Je ne comprends toujours pas ce que vous essayez d'accomplir. Vous ne pouvez pas avoir "l'adresse du pointeur" de "string s", simplement parce que "string s" n'est pas un pointeur.
Sake
@Alex: Je suppose que vous devez détecter si la chaîne est exactement la même qu'une autre que vous stockez et pas seulement si leur contenu est le même. Si tel est le cas, notez que vous pouvez utiliser l'opérateur address-of pour une référence et il vous donnera l'adresse de l'objet référencé: void f (std :: string const & s) {std :: string const * p = & s; }
David Rodríguez - dribeas
3

EDIT: J'en ai expérimenté, et j'ai découvert que les choses sont un peu plus subtiles que je ne le pensais. Voici ce que je pense maintenant être une réponse exacte.

&sn'est pas une lvalue, vous ne pouvez donc pas y créer de référence à moins que le type de la référence ne fasse référence à const. Donc, par exemple, vous ne pouvez pas faire

string * &r = &s;

mais tu peux faire

string * const &r = &s;

Si vous mettez une déclaration similaire dans l'en-tête de la fonction, cela fonctionnera.

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

Il y a un autre problème, celui des temporaires. La règle est que vous ne pouvez obtenir une référence à un temporaire que si c'est le cas const. Donc, dans ce cas, on pourrait affirmer que & s est temporaire et doit donc être déclaré constdans le prototype de fonction. D'un point de vue pratique, cela ne fait aucune différence dans ce cas. (C'est soit une valeur, soit une valeur temporaire. De toute façon, la même règle s'applique.) Cependant, à proprement parler, je pense que ce n'est pas une valeur temporaire mais une valeur. Je me demande s'il existe un moyen de faire la distinction entre les deux. (Peut-être est-il simplement défini que toutes les valeurs temporaires sont des valeurs, et toutes les valeurs non-l sont des valeurs temporaires. Je ne suis pas un expert de la norme.)

Cela étant dit, votre problème se situe probablement à un niveau supérieur. Pourquoi voulez-vous une référence à l'adresse de s? Si vous voulez une référence à un pointeur vers s, vous devez définir un pointeur comme dans

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

Si vous voulez une référence sou un pointeur vers s, faites la chose simple.

Ari
la source
1

Je viens de faire usage d'une référence à un pointeur pour rendre tous les pointeurs dans un arbre binaire supprimé sauf la racine en toute sécurité. Pour rendre le pointeur sûr, nous devons juste le mettre à 0. Je ne pouvais pas faire en sorte que la fonction qui supprime l'arbre (en ne gardant que la racine) accepte une référence vers un pointeur puisque j'utilise la racine (ce pointeur) comme premier entrée pour parcourir à gauche et à droite.

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 bout de ligne, une référence à un pointeur n'est pas valide lorsque le paramètre formel est le pointeur (this).

Mohd
la source
1

Bienvenue dans les références C ++ 11 et rvalue:

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

Vous avez maintenant accès à la valeur du pointeur (référencé par val), qui est l'adresse de la chaîne.

Vous pouvez modifier le pointeur, et personne ne s'en souciera. C'est un aspect de ce qu'est une rvalue en premier lieu.

Attention: la valeur du pointeur n'est valide que jusqu'au myfunc()retour. Enfin, c'est temporaire.

pas un utilisateur
la source
-1

Je sais qu'il est possible de passer des références de pointeurs, je l'ai fait la semaine dernière, mais je ne me souviens pas quelle était la syntaxe, car votre code semble correct pour mon cerveau en ce moment. Cependant, une autre option consiste à utiliser des pointeurs de pointeurs:

Myfunc(String** s)
Robert Gould
la source
-8

myfunc ("string * & val") cela n'a aucun sens en soi. "string * & val" implique "string val", * et & s'annule. Enfin on ne peut pas passer de variable string à une fonction ("string val"). Seuls les types de données de base peuvent être passés à une fonction, car les autres types de données doivent passer en tant que pointeur ou référence. Vous pouvez ajouter une chaîne & val ou une chaîne * val à une fonction.

Shashikiran
la source
4
Mauvais à tous les niveaux. Veuillez actualiser la syntaxe de votre déclaration var.
Ari