Понимание оптимизации возвращаемого значения и возврата временных объектов - C++

StackOverflow https://stackoverflow.com/questions/1394229

Вопрос

Пожалуйста, обратите внимание на три функции.

std::string get_a_string()
{
    return "hello";
}

std::string get_a_string1()
{
    return std::string("hello");
}

std::string get_a_string2()
{
    std::string str("hello");
    return str;
}
  1. Будет ли RVO применяться во всех трех случаях?
  2. Можно ли вернуть временное значение, как в приведенном выше коде?Я считаю, что это нормально, поскольку я возвращаю его по значению, а не возвращаю ссылку на него.

Есть предположения?

Это было полезно?

Решение

В первых двух случаях будет иметь место оптимизация RVO.RVO — старая функция, и большинство компиляторов ее поддерживают.Последний случай – это так называемый NRVO (Названный RVO).Это относительно новая функция C++.Стандарт позволяет, но не требует реализации NRVO (как и RVO), но некоторые компиляторы поддерживают его.

Подробнее о RVO можно прочитать в пункте 20 книги Скотта Мейерса. Более эффективный C++.35 новых способов улучшить ваши программы и проекты.

Здесь — хорошая статья о NRVO в Visual C++ 2005.

Другие советы

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

Во-вторых, все три случая на самом деле идентичны (поскольку в третьем случае вы все равно не получаете доступа к временному значению), и компилятор может даже выдать один и тот же код для всех из них.Следовательно, он может использовать RVO во всех трех случаях.Это полностью зависит от компилятора.

Все случаи верны.Все они создадут временный объект и применят конструктор копирования возвращаемого типа.Обязательно, если нет конструктора копирования, код завершится ошибкой.

RVO произойдет во всех трех случаях в большинстве компиляторов.Единственная разница заключается в том, что последний вариант не является обязательным по стандарту.Это потому, что у вас есть именованная переменная.Но большинство компиляторов достаточно умны, чтобы применить к нему RVO...чем позже объявлена ​​именованная переменная и чем меньше преобразований она применяется, тем выше вероятность применения RVO к именованной переменной.

Кстати, возврат ссылки, конечно, возможен, как вы могли видеть в другом коде.Чего вы не должны делать, так это возвращать ссылку на локальный объект.

std::string& get_a_string2()
{
    std::string str("hello");
    return str; //error!
}

Как вы знаете, выдаст ошибку времени компиляции.Однако,

std::string& get_a_string2(std::string& str)
{
    // do something to str
    return str; //OK
}

Будет работать нормально.В данном случае не требуется никакого построения или копирования.Просто функция возвращает ссылку на свой аргумент.

  1. Это зависит от вашего компилятора — какую платформу вы имеете в виду?Лучший способ это выяснить – составить очень небольшое тестовое приложение и проверьте ASM, который создает ваш компилятор.

  2. Да, это нормально, хотя вы никогда не упоминаете о том, что вас беспокоит;скорость?стиль?вы можете использовать локальную временную ссылку на константную ссылку - время жизни временной ссылки будет продлено до времени жизни ссылки - попробуйте и убедитесь сами!(Херб Саттер объясняет это здесь) См., например, конец поста.

ИМО, вам почти всегда лучше доверять своему компилятору, который оптимизирует ваш код за вас.Есть очень мало случаев, когда вам нужно заботиться о таких вещах (одной из таких областей является код очень низкого уровня, где вы взаимодействуете с аппаратными регистрами).

int foo() { return 42; }

int main(int, char**)
{
    const int &iRef = foo();
    // iRef is valid at this point too!
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top