Passando referências para ponteiros em C ++


130

Até onde eu sei, não há razão para eu não poder passar uma referência a um ponteiro em C ++. No entanto, minhas tentativas de fazê-lo estão falhando e não faço ideia do porquê.

Isto é o que estou fazendo:

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

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

E eu estou recebendo este erro:

não é possível converter o parâmetro 1 de 'std :: string *' para 'std :: string * &'

Respostas:


126

Sua função espera uma referência a um ponteiro de string real no escopo da chamada, não a um ponteiro de string anônimo. Portanto:

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

deve compilar muito bem.

No entanto, isso só é útil se você pretende modificar o ponteiro que você passa para a função. Se você pretende modificar a própria string, use uma referência à string, conforme sugerido por Sake. Com isso em mente, deve ser mais óbvio o motivo pelo qual o compilador reclama do seu código original. No seu código, o ponteiro é criado 'on the fly', modificando esse ponteiro não teria conseqüências e não é isso que se pretende. A idéia de uma referência (versus um ponteiro) é que uma referência sempre aponta para um objeto real.


86

O problema é que você está tentando vincular um temporário à referência, que o C ++ não permite, a menos que a referência seja const.

Portanto, você pode executar um dos seguintes procedimentos:

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;
}

É especialmente instrutivo escrever o tipo da maneira que o Sr. Burr faz no exemplo No.2 - em string* const& valvez de um equivalente menos legível como por exemplo const string* &val. Aos meus olhos, isso é claramente uma referência constante , a um ponteiro, a uma corda. Pessoalmente, prefiro escrever em T const&vez de const T&em declarações para obter esse esclarecimento exato.
usar o seguinte código

1
Nit pick (não uma crítica): Para mim, a frase bind a temporary to the referenceé mais clara nesta resposta do que @Chris 'as reference to an actual string pointer in the calling scope, not an anonymous string pointer. Independentemente disso, ambos provavelmente estão corretos da minha perspectiva.
Kevinarpe

1
@ fish2000 Não é só isso, const string*&e string* const&na verdade são tipos diferentes. O primeiro é uma não constreferência a const string*, enquanto o segundo é uma constreferência a string*.
Justin Time - Restabelece Monica

@ fish2000 tenha cuidado com "preferir" T const&a const T&- como @Justin Time aponta, eles são tipos diferentes. Pode não têm consequências, por vezes, mas em outras situações será absolutamente crítico para preferir um ou outro, muito parecido com preferindo inta double.
omatai

3
@ fish2000 - um diz "aqui está uma referência que você pode mudar para algo que não pode mudar", enquanto o outro diz "aqui está uma referência que você não pode mudar para algo que pode mudar". Neste exemplo, você simplesmente não pode trocar os dois.
omatai

10

Altere para:

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

EDITAR:

Isso é chamado ref-to-pointere você não pode passar um endereço temporário como referência para a função. (a menos que seja const reference).

Porém, eu mostrei std::string* pS = &s;(ponteiro para uma variável local), seu uso típico seria: quando você quiser que o receptor altere o ponteiro em si, não o objeto para o qual ele aponta. Por exemplo, uma função que aloca memória e atribui o endereço do bloco de memória alocado ao argumento deve fazer referência a um ponteiro, ou um ponteiro ao ponteiro:

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

}

5

&s produz ponteiro temporário para string e você não pode fazer referência a objeto temporário.


4
Isso não é bem verdade - você pode fazer uma referência a const temporário
1800 INFORMATION

3

Experimentar:

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

No entanto, para o que estou fazendo, preciso do endereço do ponteiro e do próprio ponteiro. Não quero passar o ponteiro por valor.
528 Alex

Ainda não entendo o que você tenta realizar. Você não pode ter "endereço do ponteiro" de "string s", simplesmente porque "string s" não é um ponteiro.
Sake

@ Alex: Entendo que você precisa detectar se a string é exatamente igual à outra que você está armazenando e não apenas se o conteúdo é o mesmo. Se for esse o caso, observe que você pode usar o operador address-of para uma referência e ele obterá o endereço do objeto referenciado: void f (std :: string const & s) {std :: string const * p = & s; }
David Rodríguez - dribeas

3

Edição: Eu experimentei alguns, e descobri coisas são um pouco mais sutis do que eu pensava. Aqui está o que agora acho que é uma resposta precisa.

&snão é um valor l, portanto você não pode criar uma referência a ele, a menos que o tipo da referência seja referência const. Então, por exemplo, você não pode fazer

string * &r = &s;

mas você pode fazer

string * const &r = &s;

Se você colocar uma declaração semelhante no cabeçalho da função, ela funcionará.

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

Há outra questão, a saber, temporários. A regra é que você pode obter uma referência a um temporário apenas se for const. Portanto, nesse caso, pode-se argumentar que & s é temporário e, portanto, deve ser declarado constno protótipo de função. Do ponto de vista prático, não faz diferença neste caso. (É um rvalue ou um temporário. De qualquer forma, a mesma regra se aplica.) No entanto, estritamente falando, acho que não é um temporário, mas um rvalor. Gostaria de saber se existe uma maneira de distinguir entre os dois. (Talvez seja simplesmente definido que todos os temporários são rvalores e todos os não-valores são temporários. Não sou especialista no padrão.)

Dito isto, seu problema provavelmente está em um nível superior. Por que você deseja uma referência ao endereço de s? Se você quiser uma referência a um ponteiro s, precisará defini-lo como em

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

Se você deseja uma referência sou um ponteiro s, faça o que é direto.


1

Acabei de usar uma referência a um ponteiro para tornar todos os ponteiros em uma árvore binária excluída, exceto a raiz segura. Para tornar o ponteiro seguro, basta defini-lo como 0. Eu não poderia fazer com que a função que exclui a árvore (mantendo apenas a raiz) aceite um ref para um ponteiro, pois estou usando a raiz (este ponteiro) como o primeiro entrada para percorrer esquerda e direita.

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

Bottom line, uma referência a um ponteiro é inválida quando o parâmetro formal é o (this) ponteiro.


1

Bem-vindo às referências C ++ 11 e 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);
    // ...
}

Agora você tem acesso ao valor do ponteiro (referido por val), que é o endereço da string.

Você pode modificar o ponteiro e ninguém se importa. Esse é um aspecto do que é um rvalue em primeiro lugar.

Cuidado: o valor do ponteiro é válido apenas até o myfunc()retorno. Finalmente, é temporário.


-1

Sei que é possível passar referências de ponteiros, fiz isso na semana passada, mas não me lembro qual era a sintaxe, pois seu código parece correto para o meu cérebro agora. No entanto, outra opção é usar ponteiros de ponteiros:

Myfunc(String** s)

-8

myfunc ("string * & val") isso não faz sentido. "string * & val" implica "string val", * e & cancela um ao outro. Finalmente, não é possível passar a variável string para uma função ("string val"). Somente tipos de dados básicos podem ser passados ​​para uma função, pois outros tipos de dados precisam passar como ponteiro ou referência. Você pode ter string & val ou string * val para uma função.


4
Errado em todos os níveis. Atualize sua sintaxe da declaração var.
Ari
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.