Могу ли я использовать ссылку на указатель в C ?

#c #pointers #reference #pass-by-reference #pass-by-pointer

Вопрос:

Я передаю ссылку на переменную указателя в функцию. Функция что-то сделает и укажет переменную указателя на некоторый объект. Код:

 int Foo(Obj* amp;ptr) {
  // do something...
  ptr = some_address;
  return return_value;
}

int main() {
  Obj* p = nullptr;
  int ret = Foo(p);
  // do something with ret
  p->DoSomething();
}
 

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

 int Foo(const Obj* amp;ptr) {
  // do something...
  ptr = some_address;
  return return_value;
}

int main() {
  const Obj* p = nullptr;
  int ret = Foo(p);
  // do something with ret
  p->DoSomething(); // this should only work when DoSomething() is const method
}
 

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

C выдает эту ошибку компиляции:

 main.cpp:22:10: error: cannot bind non-const lvalue reference of type ‘const Obj*amp;’ to an rvalue of type ‘const Obj*’
   22 |     Foo(ptr);
      |         ^~~
main.cpp:14:23: note:   initializing argument 1 of ‘void test(const Obj*amp;)’
   14 | int Foo(const Obj* amp;ptr) {
      |         ~~~~~~~~~~~~^~~
 

Некоторые мысли:

Ошибка не может быть воспроизведена

  1. Я полагаю, что эта ошибка отображается, когда я пытаюсь передать «неназванную переменную» в ссылочный параметр. В этом случае я передаю переменную ptr , что не должно быть проблемой.
  1. ptr передается в качестве параметра, потому что функция имеет полезное возвращаемое значение. Настройка ptr больше похожа на побочный продукт этой функции, где вызывающий может игнорировать или использовать.
  2. Я также могу попробовать использовать Obj** в качестве параметра, который передается по указателю вместо передачи по ссылке. Это работает, когда a const Obj** передается в качестве параметра. Мне просто интересно, что будет, если параметр передается по ссылке.

Комментарии:

1. Какой компилятор вы используете? Кроме того, ваши сообщения об ошибках не соответствуют вашему примеру кода. В ошибке упоминается функция с именем test , вызываемая в строке 14 main.cpp , но это явно другая программа по сравнению с тем, что вы показали.

2. В сообщении об ошибке ошибочный код указан следующим Foo(ptr) образом … Приведенный код не является тем кодом, который выдал сообщение об ошибке. Каков точный тип ptr ??

3. Этот код с тривиальной реализацией Obj added не приводит к этой ошибке.

4. Извините за путаницу. Тестовая функция , которую я назвал , была test вместо Foo . Я заменил test на Foo после копирования ошибки компиляции на SO и допустил ошибку…

5. Что касается компилятора, я изначально скомпилировал свой код, используя как clang , так и это онлайн-тестирование GDB . Однако сейчас я не могу воспроизвести эту ошибку (???) в обеих средах… Я редактирую этот вопрос в один без ошибки.

Ответ №1:

Я не уверен, в чем ваша проблема, поскольку указанный код ошибки не соответствует вашему коду.

Ваш второй пример с int Foo(const Obj* amp;ptr) работает точно так, как задумано, и отлично компилируется, если вы DoSomething задаете const .

Прокомментировать ваши три мысли:

  1. Если вы все правильно настроите, ошибка исчезнет.
  2. Мне очень, очень не нравятся такие нестандартные вопросы. Гораздо чище возвращать struct или пару int и pointer . Таким образом, вызывающий может записывать const auto[ret, p] = Foo(); и не должен явно объявлять указатель, который вы, возможно, не захотите использовать.
  3. Передача указателей на указатели выполняется в стиле C из-за отсутствия ссылок и просто усложняет чтение кода без какой-либо пользы.

Ниже приведен слегка измененный код, который отлично компилируется, с лучшим Foo, как упоминалось в моем ответе на 2.:

 #include <utility>

struct Obj
{
    void DoSomething() const;
};

// This is ugly of course, used just to have a valid ptr to return
Obj global;

int Foo(const Obj* amp;ptr) {
  // do something...
  ptr = amp;global;
  return 5;
}

std::pair<int, const Obj*> BetterFoo()
 {
  // do something...
  return {5, amp;global};
}

int main() {

  const Obj* p1 = nullptr;
  int ret1 = Foo(p1);

  const auto[ret2, p2] = BetterFoo();

  p1->DoSomething(); 
  p2->DoSomething();
}
 

Комментарии:

1. «Я не уверен, в чем ваша проблема» — обычно это причина избежать ответа и вместо этого запросить дополнительную информацию через комментарий (или проголосовать за такой комментарий, если он уже существует — что я и сделал). Цель Stack Overflow — создать хранилище вопросов и ответов для будущих посетителей. Если проблема четко не определена в вопросе, маловероятно, что будущие посетители смогут определить, относится ли вопрос к ним.

Ответ №2:

Один из способов справиться с этой ситуацией — использовать typedef или using . Например, using ObjPtr = Obj*; теперь функция будет int Foo(const ObjPtramp; ptr) { ... } .

Комментарии:

1. const ObjPtr это не то же const Obj * самое , что . Первый представляет собой постоянный указатель на непостоянный объект, в то время как второй является непостоянным указателем на постоянный объект. OP специально запросил указатель на const, который ObjPtr не может быть доставлен.

2. Как писал @JaMiT, делая это, вы делаете сам указатель const — что означает, что невозможно переназначить указатель внутри Foo. Таким образом, с вашим «решением» функция Foo больше не будет компилироваться.