2009-05-05 13 views
96

O ile mogę powiedzieć, nie ma powodu, dla którego nie powinienem mieć możliwości przekazania odniesienia do wskaźnika w C++. Jednak moje próby tego nie udają się i nie mam pojęcia dlaczego.Przekazywanie odniesień do wskaźników w C++

To, co robię:

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

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

A ja dostaję ten błąd:

cannot convert parameter 1 from 'std::string *' to 'std::string *&'

Odpowiedz

91

Twoja funkcja oczekuje odniesienia do rzeczywistego wskaźnika w zasięgu, a nie anonimowego wskaźnika łańcucha. Tak więc:

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

powinien skompilować dobrze.

Jest to jednak przydatne tylko wtedy, gdy zamierzasz zmodyfikować wskaźnik, który przekazujesz do funkcji. Jeśli zamierzasz zmodyfikować sam napis, powinieneś użyć odnośnika do łańcucha, tak jak zasugerował Sake. Mając to na uwadze, powinno być bardziej oczywiste, dlaczego kompilator narzeka na twój oryginalny kod. W twoim kodzie wskaźnik jest tworzony "w locie", modyfikując, że wskaźnik nie będzie miał konsekwencji, a to nie jest zamierzone. Idea odniesienia (a wskaźnik) polega na tym, że odniesienie zawsze wskazuje na rzeczywisty obiekt.

3

Spróbuj:

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

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

lub

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

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

Do tego, co robię, potrzebuję adresu wskaźnika oraz samego wskaźnika. Nie chcę przekazywać wskaźnika przez wartość. – Alex

+0

Nadal nie rozumiem, co próbujesz osiągnąć. Nie możesz mieć "adresu wskaźnika" ciągu "s", po prostu dlatego, że "ciąg s" nie jest wskaźnikiem. – Sake

+0

@Alex: Rozumiem, że potrzebujesz wykryć, czy ciąg jest dokładnie taki sam, jak inny, który przechowujesz, a nie tylko to, czy ich zawartość jest taka sama. W takim przypadku zwróć uwagę, że możesz użyć operatora adresu do referencji, a otrzymasz adres obiektu, do którego się odwołujesz: void f (std :: string const & s) {std :: string const * p = & s; } –

7

go zmienić na:

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

EDIT:

To się nazywa ref-to-pointer i nie można przekazać tymczasowy adres jako punkt odniesienia do funkcjonowania. (chyba że jest to const reference).

Chociaż pokazałem std::string* pS = &s; (wskaźnik do zmiennej lokalnej), jego typowym zastosowaniem będzie: , gdy chcesz, aby wywoływacz zmienił sam wskaźnik, a nie obiekt, do którego wskazuje. Na przykład, funkcja, która przydziela pamięć i przypisuje adres bloku pamięci przydzielonej jej argumentu musi mieć odniesienie do wskaźnika lub wskaźnik do wskaźnika:

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

} 
-1

wiem, że to posible przekazać referencje wskaźników, zrobiłem to w zeszłym tygodniu, ale nie pamiętam, jaka była składnia, ponieważ twój kod wygląda teraz poprawnie na mój mózg. Jednak inną opcją jest wykorzystanie wskaźników wskaźników:

Myfunc(String** s) 
5

&s produkuje tymczasowy wskaźnik do łańcucha i nie można nawiązywać do tymczasowego obiektu.

+4

To nie jest do końca prawdą - możesz odwołać się do const temporary –

75

Problem polega na tym, że próbujesz powiązać tymczasowy plik z referencją, którego C++ nie zezwala, chyba że odwołanie to jest const.

Więc można zrobić jedną albo następuje:

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

Szczególnie pouczające jest napisanie tego, jak robi to Mr. Burr w przykładzie nr 2 - 'string * const & val' zamiast mniej czytelnego odpowiednika jak np 'ciąg ciąg * i wal'. W moich oczach jest to wyraźnie const -reference_, do wskaźnika, do napisu. Osobiście wolę pisać 'T const &' zamiast 'const T & 'w deklaracjach, aby uzyskać to dokładne wyjaśnienie. – fish2000

+1

Nit pick (not a criticism): Dla mnie wyrażenie "bind a temporary to reference" jest wyraźniejsze w tej odpowiedzi niż @Chris "jako" odwołanie do rzeczywistego wskaźnika w zasięgu, a nie anonimowy wskaźnik łańcucha " . Niezależnie od tego, obie są prawdopodobnie poprawne z mojej perspektywy. – kevinarpe

+1

@ fish2000 Co więcej, 'const string * &' oraz 'string * const &' są w rzeczywistości różnymi typami. Pierwszym jest odwołanie do 'const string *', podczas gdy drugie jest odwołaniem 'const' do' string * '. –

2

EDIT: Ja eksperymentowałem trochę i odkrył rzeczą są nieco subtelniejszy niż myślałem.Oto, co teraz uważam za dokładną odpowiedź.

&s nie jest lwartością, więc nie można utworzyć odwołania do niego, chyba że typ odniesienia odnosi się do const. Tak na przykład, nie można zrobić

string * &r = &s; 

ale można zrobić

string * const &r = &s; 

Jeśli umieścisz podobną deklarację w nagłówku funkcji, to będzie działać.

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

Istnieje inna kwestia, a mianowicie tymczasowe. Zasadą jest, że można uzyskać odniesienie do tymczasowego tylko wtedy, gdy jest to const. Tak więc w tym przypadku można argumentować, że & s jest tymczasowe, a więc musi zostać zadeklarowane jako prototyp funkcji const. Z praktycznego punktu widzenia nie ma w tym przypadku znaczenia. (Jest to albo wartość rubliczna, albo tymczasowa. Tak czy inaczej, obowiązuje ta sama zasada.) Jednakże, ściśle rzecz biorąc, uważam, że nie jest to tymczasowa, lecz rwaluta. Zastanawiam się, czy istnieje sposób na rozróżnienie tych dwóch. (Być może jest to po prostu zdefiniowane, że wszystkie tymczasowe są rwartościami, a wszystkie nie-l-wartości są tymczasowymi.) Nie jestem ekspertem od standardu.)

W związku z tym twój problem jest prawdopodobnie na wyższym poziomie. Dlaczego chcesz odwołać się do adresu s? Jeśli chcesz odniesienie do wskaźnika do s, trzeba zdefiniować jako wskaźnik w

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

Jeśli chcesz odniesienie do s lub wskaźnik do s, wykonaj prostą rzeczą.

-8

myfunc ("string * & val") samo to nie ma żadnego sensu. "string * & val" oznacza "ciąg znaków", * i & anuluje się nawzajem. Wreszcie nie można przekazać zmiennej łańcuchowej do funkcji ("string val"). Tylko podstawowe typy danych mogą być przekazywane do funkcji, ponieważ inne typy danych muszą być przekazywane jako wskaźnik lub odwołanie. Możesz mieć ciąg znaków & val lub string * val dla funkcji.

+3

Niepoprawny na wszystkich poziomach. Odśwież swoją składnię deklaracji var. – Ari

1

Właśnie skorzystałem z odniesienia do wskaźnika, aby wszystkie wskaźniki w usuniętym drzewie binarnym, z wyjątkiem sejfu root. Aby wskaźnik był bezpieczny, musimy ustawić go na 0. Nie mogłem wykonać funkcji, która usuwa drzewo (zachowując tylko root), aby zaakceptować ref do wskaźnika, ponieważ używam root'a (ten wskaźnik) jako pierwszego wejście do przechodzenia w lewo i w prawo.

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 

Dolna linia, odniesienie do wskaźnika jest nieprawidłowe, gdy parametr formalny jest (tym) wskaźnikiem.

0

Witamy w C++ 11 i rvalue odnośnikach:

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

Teraz masz dostęp do wartości wskaźnika (określonych przez val), który jest adres łańcucha.

Możesz modyfikować wskaźnik i nikt się tym nie przejmuje. Jest to jeden z aspektów tego, co jest rwartością.

Należy zachować ostrożność: Wartość wskaźnika jest ważna do momentu powrotu do wartości myfunc(). W końcu jest tymczasowy.

Powiązane problemy