Является ли RVO (оптимизация возвращаемого значения) для неназванных объектов универсально гарантированным поведением? - PullRequest
5 голосов
/ 07 декабря 2011

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

Однако этот термин «разрешено» сбивает с толку. Означает ли это, что RVO гарантированно произойдет на каждом компиляторе. Из-за изменений в коде ниже RVO наблюдается наблюдаемое поведение:

#include<iostream>
int global = 0;
struct A {
  A(int *p) {}
  A(const A &obj) { ++ global; }
};

A foo () {  return A(0); }  // <--- RVO happens
int main () {
  A obj = foo(); 
  std::cout<<"global = "<<global<<"\n"; // prints 0 instead of 2
}

Предполагается ли, что эта программа напечатает global = 0 для всех реализаций независимо от оптимизации компилятора и размера метода foo?

Ответы [ 3 ]

4 голосов
/ 07 декабря 2011

В соответствии со стандартом программа может печатать 0, 1 или 2. Конкретный абзац в C ++ 11 - это 12.8p31, который начинается с:

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

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

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

2 голосов
/ 07 декабря 2011

Это не может быть гарантировано.Если вы попытаетесь написать такую ​​гарантию согласованно, вы не сможете это сделать.

Например, рассмотрим следующий код:

std::string f() {
  std::string first("first");
  std::string second("second");
  return FunctionThatIsAlwaysFalse() ? first : second;
}

Функция FunctionThatIsAlwaysFalse всегда возвращает false, но вы можете сказать это, только если выполняете межмодульную оптимизацию.Должен ли стандарт требовать от каждого отдельного компилятора межмодульной оптимизации, чтобы он мог использовать RVO в этом случае?Как это будет работать?Или это должно запретить любому компилятору использовать RVO, когда необходима межмодульная оптимизация?Как это будет работать?Как это может остановить компиляторы, которые достаточно умны, чтобы видеть, что RVO возможно делать это, и те, которые не делают этого?

Должен ли стандартный список, который каждый компилятор оптимизации должен поддерживать с RVO?И должен ли он запретить RVO в других случаях?Разве это не победило бы с точки зрения оптимизации компиляторов?

А как насчет случаев, когда компилятор считает, что RVO снизит производительность?Нужно ли компилятору выполнять оптимизацию, которую он считает плохой?Например:

if(FunctionCompilerKnowsHasNoSideEffectsAndThinksMostlyReturnsFalse())
  return F(3); // Here RVO is a pessimization
else
{
 Foo j=F(3);
 return Foo(j);
}

Здесь, если компилятору не требуется выполнять RTO, можно избежать if и вызова функции, поскольку без RTO код одинаков в обеих половинах.Если вы заставите компилятор выполнить оптимизацию, которая, по его мнению, ухудшит ситуацию?Почему?

Нет действительно способа заставить такую ​​гарантию работать.

2 голосов
/ 07 декабря 2011

Педантично говоря, его реализация определена. Современные компиляторы достаточно умны для такой оптимизации.

Но нет никакой гарантии, что поведение будет одинаковым во всех реализациях. Вот что такое определяемое реализацией поведение.

«разрешено» в этом контексте означает, что 0 или 1 или 2 являются стандартными соответствующими выходами.

...