Применима ли RVO (оптимизация возвращаемого значения) ко всем объектам? - PullRequest
43 голосов
/ 29 сентября 2011

Гарантируется ли RVO ( Оптимизация возвращаемого значения ) или применимо для всех объектов и ситуаций в компиляторах C ++ (особенно GCC)?

Если ответ «нет», каковы условия этой оптимизации для класса / объекта?Как я могу заставить или побудить компилятор сделать RVO для определенного возвращаемого значения?

Ответы [ 5 ]

41 голосов
/ 29 сентября 2011

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

В случае RVO (где возвращается временное) это условие выполняется тривиально: объект создается в операторе return, и, в общем, он возвращается.

В случае NRVO вам придется проанализировать код, чтобы понять, может ли компилятор знать эту информацию или нет. Если анализ функции прост, есть вероятность, что компилятор ее оптимизирует (например, один оператор возврата, который не содержит условного выражения; несколько операторов возврата одного и того же объекта; несколько операторов возврата, таких как T f() { if (condition) { T r; return r; } else { T r2; return r2; } }, когда компилятор знает, что r или r2 будет возвращено ...)

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

std::string f( bool x ) {
   std::string a("a"), b("b");
   if ( x ) return a; 
   else return b;
}

Может быть переписан компилятором в:

std::string f( bool x ) {
   if ( x ) {
      std::string a("a"), b("b");
      return a;
   } else {
      std::string a("a"), b("b");
      return b;
   }
}

И компилятор может знать в это время, что в первом ответвлении a должен быть создан вместо возвращаемого объекта, а во втором ответвлении то же самое относится к b. Но я бы на это не рассчитывал. Если код сложный, предположим, что компилятор не сможет произвести оптимизацию.

РЕДАКТИРОВАТЬ : Есть один случай, который я не упомянул явно, компилятору не разрешено (в большинстве случаев, даже если это было разрешено, он не мог этого сделать), чтобы оптимизировать копию из аргумент функции для оператора return:

T f( T value ) { return value; } // Cannot be optimized away --but can be converted into
                                 // a move operation if available.
5 голосов
/ 29 сентября 2011

Гарантируется ли RVO (оптимизация возвращаемого значения) для всех объектов в компиляторах gcc?

Оптимизация не возможна никогда гарантировано (хотя RVO достаточно надежен, существуют некоторые случаи, которые отбрасывают его ).

Если ответ «нет», каковы условия этой оптимизации для класса / объекта?

Детали реализации, которые вы намеренно абстрагировали от вас.

Не знайте и не заботьтесь об этом, пожалуйста.

3 голосов
/ 10 декабря 2011

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

Если происходит RVO, копия избегается, и вам не нужно писатьбольше строк кода.

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

Так что «Это может уменьшить мои строки кода, если это гарантировано.Не так ли?не значит что масуд это дебилК сожалению для него, однако, RVO не гарантируется.Вы должны проверить, если это произойдет, и если это не так, напишите строительные леса и загрязните ваш дизайн.Это не может быть герпес.

2 голосов
/ 11 сентября 2013

Ссылка на R-значение (новая функция в C ++ 11) - решение вашей проблемы, которое позволяет вам явно использовать Type(Type &&r); (конструктор перемещения ), вместо Type(const Type &r) ( конструктор копирования ).

Например:

class String {
  public:    
    char *buffer;

    String(const char *s) { 
      int n = strlen(s) + 1;
      buffer = new char[n];
      memcpy(buffer, s, n);
    }

    ~String() { delete [] buffer; }

    String(const String &r) { 
      // traditional copy ...
    }

    String(String &&r) {
      buffer = r.buffer; // O(1), No copying, saves time.
      r.buffer = 0;
    }
};

String hello(bool world) {
  if (world) {
    return std::move( String("Hello, world.") );
  } else {
    return std::move( String("Hello.") );
  }
}

int main() {
  String foo = hello();
  std::cout <<foo.buffer <<std::endl;
}

И это не вызовет конструктор копирования .

EDIT1

код

return std::move( String(...) );

может быть сокращено до

return String(...);

Поскольку временным объектом по умолчанию является R-value-ref.

0 голосов
/ 29 сентября 2011

У меня нет ответа «да» или «нет», но вы говорите, что вы можете написать меньше строк кода, если оптимизация, которую вы ищете, гарантирована.

Если вы напишите код, который вам нужно написать, программа всегда будет работать, а если есть оптимизация, она будет работать быстрее. Если действительно есть случай, когда оптимизация «заполняет пробел» в логике, а не в механике кода, и заставляет ее работать или полностью меняет логику, это похоже на ошибку, которую я бы хотел исправить, а не детали реализации, на которые я бы хотел положиться или использовать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...