Передача ссылок на указатели в C ++

130

Насколько я могу судить, нет никаких причин, по которым мне нельзя разрешать передавать ссылку на указатель в C ++. Однако мои попытки сделать это терпят неудачу, и я не знаю почему.

Вот что я делаю:

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

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

И я получаю эту ошибку:

невозможно преобразовать параметр 1 из 'std :: string *' в 'std :: string * &'

Alex
источник

Ответы:

126

Ваша функция ожидает ссылки на фактический указатель на строку в вызывающей области, а не на анонимный указатель на строку. Таким образом:

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

должен компилироваться нормально.

Однако это полезно только в том случае, если вы намереваетесь изменить указатель, который вы передаете функции. Если вы собираетесь изменить саму строку, вам следует использовать ссылку на строку, как предлагает Саке. Имея это в виду, должно быть более очевидно, почему компилятор жалуется на ваш исходный код. В вашем коде указатель создается «на лету», изменение этого указателя не будет иметь никаких последствий, и это не то, что предполагается. Идея ссылки (а не указателя) заключается в том, что ссылка всегда указывает на реальный объект.

Крис
источник
86

Проблема в том, что вы пытаетесь привязать временный объект к ссылке, что C ++ не позволяет, если ссылка не является const.

Итак, вы можете выполнить одно из следующих действий:

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;
}
Майкл Берр
источник
Особенно поучительно написать шрифт так, как это делает мистер Берр в примере № 2 - string* const& valвместо менее разборчивого эквивалента, например, например const string* &val. На мой взгляд, это явно константная ссылка на указатель, на строку. Я лично предпочитаю писать, T const&а не const T&декларировать, чтобы получить это точное разъяснение.
fish2000 02
1
Nit pick (не критика): Мне bind a temporary to the referenceэтот ответ яснее, чем @Chris 'as reference to an actual string pointer in the calling scope, not an anonymous string pointer. Тем не менее, с моей точки зрения, оба варианта верны.
kevinarpe
1
@ fish2000 Не только это, const string*&а string* const&на самом деле разные типы. Первый - это не constссылка на a const string*, а второй - constссылка на a string*.
Джастин Тайм - Восстановить Монику
@ fish2000 остерегаться «желаемая» , T const&чтобы const T&- в качестве @Justin времени указывает, что различные типы. Он не может иметь никаких последствий иногда, но в других ситуациях это будет абсолютно необходимо , чтобы предпочесть один или другой, так же, как предпочитает , intчтобы double.
omatai
3
@ fish2000 - один говорит: «вот ссылка, которую вы можете изменить на то, что вы не можете изменить», а другая - «вот ссылка, которую вы не можете изменить на то, что вы можете изменить». В данном примере вы просто не можете их поменять местами.
omatai 06
10

Измените его на:

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

РЕДАКТИРОВАТЬ:

Это вызывается, ref-to-pointerи вы не можете передать временный адрес в качестве ссылки на функцию. (если это не так const reference).

Хотя я показал std::string* pS = &s;(указатель на локальную переменную), его типичное использование будет следующим: когда вы хотите, чтобы вызываемый объект изменял сам указатель, а не объект, на который он указывает. Например, функция, которая выделяет память и назначает адрес блока памяти, который она выделила своему аргументу, должна принимать ссылку на указатель или указатель на указатель:

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

}
Aj.
источник
5

&s создает временный указатель на строку, и вы не можете ссылаться на временный объект.

n0rd
источник
4
Это не совсем так - вы можете сделать ссылку на const временный
1800 ИНФОРМАЦИЯ
3

Пытаться:

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

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

или

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

// sometime later 
{
    // ...
    string s;
    myfunc(&s);
    // ...
}
Ради
источник
Однако для того, что я делаю, мне нужен адрес указателя, а также сам указатель. Я не хочу передавать указатель по значению.
Alex
Все еще не понимаю, чего вы пытаетесь достичь. У вас не может быть «адрес указателя» для «строки s» просто потому, что «строка s» не является указателем.
Саке
@Alex: Я так понимаю, вам нужно определить, совпадает ли строка с другой, которую вы храните, а не только то, одинаково ли их содержимое. Если это так, обратите внимание, что вы можете использовать оператор адресации для ссылки, и он даст вам адрес указанного объекта: void f (std :: string const & s) {std :: string const * p = & s; }
Дэвид Родригес - дрибес
3

РЕДАКТИРОВАТЬ: Я экспериментировал с некоторыми, и обнаружил, что это немного тоньше, чем я думал. Вот что я считаю точным ответом.

&sне является lvalue, поэтому вы не можете создать ссылку на него, если тип ссылки не является ссылкой const. Так, например, вы не можете делать

string * &r = &s;

но ты можешь сделать

string * const &r = &s;

Если вы поместите аналогичное объявление в заголовок функции, она заработает.

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

Есть еще одна проблема, а именно временные. Правило состоит в том, что вы можете получить ссылку на временное, только если оно есть const. Таким образом, в этом случае можно утверждать, что & s является временным и поэтому должен быть объявлен constв прототипе функции. С практической точки зрения это не имеет значения. (Это либо r-значение, либо временное. В любом случае применяется то же правило.) Однако, строго говоря, я думаю, что это не временное, а r-значение. Интересно, есть ли способ провести различие между ними. (Возможно, это просто определено, что все временные значения являются rvalue, а все значения, отличные от lvalue, являются временными. Я не эксперт по стандарту.)

При этом ваша проблема, вероятно, на более высоком уровне. Зачем нужна ссылка на адрес s? Если вам нужна ссылка на указатель s, вам нужно определить указатель, как в

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

Если вам нужна ссылка sили указатель на s, сделайте простую вещь.

Ari
источник
1

Я только что использовал ссылку на указатель, чтобы сделать все указатели в удаленном двоичном дереве, кроме корневого, безопасными. Чтобы сделать указатель безопасным, нам просто нужно установить его в 0. Я не мог заставить функцию, которая удаляет дерево (сохраняя только корень), принимать ссылку на указатель, поскольку я использую корень (этот указатель) в качестве первого ввод для перемещения влево и вправо.

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

В конце концов, ссылка на указатель недействительна, если формальным параметром является указатель (this).

Мохд
источник
1

Добро пожаловать в C ++ 11 и ссылки на 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);
    // ...
}

Теперь у вас есть доступ к значению указателя (на который указывает val), который является адресом строки.

Вы можете изменить указатель, и это никого не волнует. Это в первую очередь один из аспектов того, что такое rvalue.

Будьте осторожны: значение указателя действительно только до myfunc()возврата. Наконец, это временное явление.

не-а-пользователь
источник
-2

Я знаю, что можно передавать ссылки на указатели, я сделал это на прошлой неделе, но я не могу вспомнить, какой был синтаксис, так как ваш код сейчас кажется мне правильным. Однако другой вариант - использовать указатели указателей:

Myfunc(String** s)
Роберт Гулд
источник
-8

myfunc ("string * & val") это само по себе не имеет никакого смысла. "string * & val" подразумевает "string val", * и & отменяют друг друга. Наконец, в функцию нельзя передать строковую переменную ("string val"). В функцию можно передавать только базовые типы данных, другие типы данных необходимо передавать как указатель или ссылку. Для функции вы можете использовать строку & val или строку * val.

Shashikiran
источник
4
Неправильно на всех уровнях. Обновите синтаксис объявления var.
Ари