Является ли более эффективным возвращение ссылки const

например.

Что лучше всего из них:

std::string f() {} 

или

const std::string& f() {}

Ответ 1

Функция никогда не должна возвращать ссылку на локальный объект/переменную, поскольку такие объекты выходят из области видимости и уничтожаются при возврате из функции.

Иными словами, функция может возвращать постоянную или неконстантную ссылку на объект, область действия которого не ограничена контекстом функции. Типичным примером является пользовательский operator<<:

std::ostream & operator<<(std::ostream &out, const object &obj)
{
   out << obj.data();
   return out;
}

К сожалению, возврат по значению имеет свой недостаток производительности. Как упоминал Крис, возвращение объекта по значению включает в себя копию временного объекта и его последующее уничтожение. Копирование выполняется с помощью либо конструктора копирования, либо operator=. Чтобы избежать этой неэффективности, умные компиляторы могут применять оптимизации RVO или NRVO, но в некоторых случаях они не могут - многократные возвраты.

В готовящемся стандарте С++ 0x, частично доступном в gnu gcc-4.3, вводится ссылка на rvalue [& & amp;], которая может использоваться для различения lvalue от ссылки на rvalue. Благодаря этому можно реализовать конструктор перемещения, полезный для возврата объекта, частично избегая затрат на конструктор копирования и деструктор временного объекта.

Конструктор перемещения - это то, что Андрей задумал несколько лет назад в статье http://www.ddj.com/database/184403855, предложенной Крисом.

Конструктор перемещения имеет следующую подпись:

// move constructor
object(object && obj)
{}

и предполагается, что он переходит во владение внутренними объектами переданного объекта, оставляя последний в состоянии по умолчанию. Благодаря этому можно избежать копий внутренних органов и упростить уничтожение временных. Типичная фабрика функций будет иметь следующую форму:

object factory()
{
    object obj;
    return std::move(obj);
}

std::move() возвращает ссылку на значение из объекта. И последнее, но не менее важное, конструкторы перемещения позволяют возвращать ссылку на неопределяемые объекты.

Ответ 2

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

  • Оптимизация возвращаемого значения (RVO): вы возвращаетесь по значению, но исключаете копирование, имея только один оператор return, который создает возвращаемое значение на месте. Вот пример использования RVO: Как я могу токенизировать строку С++?

  • Именованная оптимизация возвращаемых значений (NRVO): вы возвращаетесь по значению и сначала объявляете переменную возвращаемого значения в верхней части функции. Все операторы return возвращают эту переменную. С компиляторами, которые поддерживают NRVO, эта переменная выделяется в слоте возвращаемого значения и не копируется при возврате. например.

    string
    foobar()
    {
        string result;
        // fill in "result"
        return result;
    }
    
  • Используйте shared_ptr или тому подобное как возвращаемый тип; это требует создания вашего объекта в куче, а не в стеке. Это предотвращает проблемы с обвязывающей ссылкой, но при этом не требуется копировать весь объект, просто умный указатель.

Кстати, я не могу взять кредит на информацию о RVO и NRVO; они выходят прямо из Скотта Мейера Более эффективный С++. Поскольку в настоящее время у меня нет книги со мной, любые ошибки в моем описании - это мои действия, а не Скотта.: -)

Ответ 3

Если вы вернете ссылку на переменную, которая является локальной для этой функции, тогда вы столкнетесь с проблемами (в зависимости от компилятора и его параметров).

Если вы вернете ссылку на экземпляр, который все еще находится в области видимости, когда функция возвращается, то она будет быстрее, так как копия строки не создается.

Таким образом, последний более эффективен (технически), но может работать не так, как ожидалось, в зависимости от того, на что вы возвращаете ссылку.