Когда должен быть RVO? - PullRequest
8 голосов
/ 08 января 2010

Из следующего кода: если произошла RVO, я ожидаю увидеть 2 адреса, указывающие на одно и то же местоположение, однако это не так (мой компилятор - MS VC9.0)

#include <iostream>
#include <string>

std::string foo(std::string& s)
{
   std::cout << "address: " << (unsigned int)(&s) << std::endl;
   return s;
}

int main()
{
   std::string base = "abc";
   const std::string& s = foo(base);
   std::cout << "address: " << (unsigned int)(&s) << std::endl;
   std::cout << s << std::endl;
   return 0;
}

При каких условиях должен происходить RVO?

Кстати, я основываю свой вопрос на следующем обсуждении: http://cpp -next.com / archive / 2009/08 / want-speed-pass-by-value /

Ответы [ 4 ]

8 голосов
/ 08 января 2010

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

std::string foo() {
  return std::string("hello world"); // RVO
}

std::string foo() {
  std::string str("hello world");
  bar();
  return str; // Not RVO
}

std::string foo(std::string str) {
  return str; // Not RVO
}

Более общая версия - NRVO (оптимизация именованных возвращаемых значений), который также работает с именованными переменными.

std::string foo() {
  std::string str("hello world");
  bar();
  return str; // NRVO
}

std::string foo(std::string str) {
  return str; // Not NRVO, as far as I know. The string is constructed outside the function itself, and that construction may be elided by the compiler for other reasons.
}

std::string foo(std::string str) {
  std::string ret;
  swap(ret, str);
  return ret; // NRVO. We're returning the named variable created in the function
}
4 голосов
/ 08 января 2010

Правильный ответ: «всякий раз, когда компилятор пожелает». Такое поведение не является обязательным (но допускается) стандартом, и точные условия, в которых оно срабатывает, различаются в зависимости от компилятора и от версии к версии.

Как правило, компилятор умнее вас и работает в ваших интересах. Не сомневайся.

rvalue ссылки в C ++ 0x являются своего рода ручной версией RVO.

Редактировать: Если присмотреться к своему коду, вы определенно не понимаете RVO. Поскольку ваш параметр является ссылкой, возвращаемое значение функции не может иметь такой же адрес.

2 голосов
/ 08 января 2010

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

Для меня следующее показало один и тот же адрес для обоих:

#include <iostream>
#include <string>

std::string foo()
{
   std::string s("rvo!");
   std::cout << "address: " << (void *)(&s) << std::endl;
   return s;
}

int main()
{
   const std::string s = foo();
   std::cout << "address: " << (void *)(&s) << std::endl;
   std::cout << s << std::endl;
   return 0;
}

Подписаться на комментарий Дэрида

Кодовая панель для страницы документирует, что для нее используются конструкторы -fno-elide-для C ++. Документация для этой опции формирует состояние страницы руководства g ++:

Стандарт C ++ позволяет реализации исключать создание временный, который используется только для инициализации другого объекта того же типа. Указание этой опции отключает эту оптимизацию, и заставляет G ++ вызывать конструктор копирования во всех случаях.

На моей машине компиляция с -fno-elide-constructors предотвращает RVO, но компиляция без позволяет.

1 голос
/ 08 января 2010

Вы, похоже, неправильно поняли RVO, попробуйте этот пример (на самом деле NRVO) :

std::string foo(const char* const s)
{
    std::string out(s);
    std::cout << "address: " << (void*)(&out) << std::endl;
    return out;
}

int main()
{
   std::string s = foo("abc");
   std::cout << "address: " << (void*)(&s) << std::endl;
}
...