Подтвердить что ты не робот

Является ли RVO (Оптимизация возвращаемого значения) применимым ко всем объектам?

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

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

4b9b3361

Ответ 1

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

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

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

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

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. Но я не стал бы на это рассчитывать. Если код является сложным, предположим, что компилятор не сможет произвести оптимизацию.

EDIT: есть один случай, о котором я не упоминал явно, компилятор не разрешен (в большинстве случаев, даже если он был разрешен, он не мог этого сделать), чтобы оптимизировать копию от аргумента функции к оператору return:

T f( T value ) { return value; } // Cannot be optimized away --but can be converted into
                                 // a move operation if available.

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

Ссылка на R-значение (новая функция С++ 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(...);

Так как объект temp по умолчанию имеет значение R-value-ref.

Ответ 5

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

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