Мы все это знаем возвращать ссылку на локальную переменную — плохая идея. Тем не менее, мне интересно, будет ли вообще когда-либо хорошей идеей возвращать ссылку и можно ли определить некоторые хорошие правила о том, когда или когда этого не делать.
Моя проблема с возвратом ссылки состоит в том, что вызывающая функция должна заботиться о времени жизни объекта, который не должен быть ее ответственностью. В качестве надуманного примера:
#include <vector>
const int& foo() {
std::vector<int> v = {1, 2, 3, 4, 5};
return v[0];
}
int main(int argc, const char* argv[])
{
const int& not_valid = foo();
return 0;
}
Здесь vector
выходит за рамки в конце foo
уничтожение его содержимого и аннулирование любых ссылок на его элементы. vector::operator[]
возвращает ссылку на элемент, и поэтому, когда эта ссылка в дальнейшем возвращается из foo
Ссылка в main
болтается Я не верю, что ссылка на const продлит здесь срок службы, потому что это не ссылка на временную.
Как я уже сказал, это надуманный пример и автор foo
наверное, не будет так глупо пытаться вернуться v[0]
в качестве ссылки. Тем не менее, легко увидеть, что для возврата ссылки требуется, чтобы вызывающая сторона заботилась о времени жизни объекта, которому она не принадлежит. Толкая элемент в vector
копирует его, так что тогда vector
несет ответственность за это. Эта проблема не существует для передачи ссылочного аргумента, потому что вы знаете, что функция завершится до того, как вызывающая программа продолжит работу и уничтожит объект.
Я вижу, что возвращая ссылку, можно получить красивый синтаксис v[0] = 5
— но что такого плохого в том, чтобы иметь функцию-член v.set(index, value)
? По крайней мере, с этим мы не будем выставлять внутренние объекты. Я знаю, что может быть увеличение производительности при возврате ссылки, но с РВО, Именуется RVO (NRVO), а семантика перемещения либо незначительна, либо вообще отсутствует.
Поэтому я пытался представить, в каких ситуациях возвращение ссылки действительно безопасно, но я не могу разобраться во всех различных сочетаниях семантики владения, которые могут возникнуть. Есть ли хорошие правила, когда это делать?
Примечание: я знаю лучший способ борьбы с собственностью в vector
s использовать интеллектуальные указатели, но тогда возникает другая проблема с другим объектом — кому принадлежит интеллектуальный указатель?
Существует множество хороших способов использования ссылки. Один из них, как вы сказали, эмулировать что-то вроде собственного оператора разыменования:
struct Goo
{
int & operator[](size_t i) { return arr[i]; }
int & front() { return arr[0]; }
// etc.
private:
int * arr;
};
Другой вариант использования — когда вы возвращаете ссылку на переданную вещь. Типичным примером является цепная операция, такая как <<
:
std::ostream & operator<<(std::ostream & os, Goo const & g)
{
return os << g[3];
}
В качестве последнего примера приведем поточно-ориентированный глобальный объект:
Goo & get_the_object()
{
static Goo impl;
return impl;
}
Ссылки являются неотъемлемой частью языка, и они вполне могут быть возвращены вызовом функции. Как вы сказали, важно понимать время жизни объектов, но это всегда верно, а не особая проблема возврате Рекомендации.
Лично мне нравится возвращать ссылки на статические переменные, когда я хочу реализовать шаблон Singleton
SomeClass& getTheSingleton()
{
static SomeClass theInstance;
return theInstance;
}
Мне не нужно писать какую-либо логику, касающуюся инициализации какого-либо указателя, и это дает мне некоторый контроль над порядком статической инициализации