Работает ли оптимизация возвращаемого значения при назначении другому типу? - PullRequest
4 голосов
/ 24 апреля 2011

Рассмотрим следующие два класса:

class Base  
{  
  Base(const Base& other) {...} // relatively expensive operations here...
  Base(int i)             {...} // ...here,
  virtual ~Base()         {...} // ...and here
  ...
};

class Derived : public Base
{
  ...
  Derived(const Base& other)   :Base(other) {...} // some typechecking in here 
  virtual ~Derived() {}
  ...
};

Это означает, что База может быть "повышена" с помощью второго конструктора из Производная . Теперь рассмотрим следующий код:

Base getBase()  
{
   int id = ...
   return Base(id);
}
...
int main()
{
   Base    b = getBase();   // CASE 1
   Derived d1(b);           // "upcast"

   Derived d2 = getBase();  // CASE 2
   ...
}

Я использую VS2008 с включенными оптимизациями (/ Ox / Ob2 / Oi / Ot). Я проверил вызовы конструкторов и деструкторов на консольном выводе:

В Случай 1 работает оптимизация возвращаемого значения. Есть два вызова:

  1. Base (INT)
  2. ~ Base ()

Однако здесь нечего выиграть, когда Derived -объект необходим в main . Для "upcast" требуется другая пара конструктор / деструктор.

В Случай 2 Оптимизация возвращаемого значения не работает. Здесь создаются и уничтожаются два объекта:

  1. Base (int) // Создать временную
  2. ~ Base () // Уничтожить временно
  3. База (константная база &) // через производную (константная база &)
  4. ~ Base () // через ~ Derived ()

Теперь мне кажется, что у меня есть три противоречивых требования:

  1. Я бы хотел избежать накладных расходов на создание временного объекта (поскольку создание и уничтожение объекта довольно дорого в классе Base )
  2. В main мне нужен Derived -объект вместо Base -объекта для работы с.

Очевидно, здесь нет бесплатного обеда. Но я мог что-то пропустить. Итак, мой вопрос: есть ли способ объединить эти требования? Или у кого-нибудь был подобный опыт?

Sidenote: Мне известен тот факт, что производный "upcast" (const Base и другие) может завершиться ошибкой во время выполнения (об этом позаботились). Поскольку на синтаксическом уровне код в порядке, я думаю, что это не причина для компилятора избегать RVO.

Ответы [ 2 ]

2 голосов
/ 24 апреля 2011

Это плохо.

Derived(const Base& other)   :Base(other) {...}

Статический тип other может принадлежать производному типу. В этом случае это будет нарезано. Поверх этого базового класса все равно будет скопировано.

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

Вместо Derived(const Base& other) вы можете рассмотреть другой подход. Как насчет этого:

class Base  
{
  ...
  // extract expensive parts of another instance
  virtual void initialise(Base& b);
  ...
};

class Derived : public Base
{
  ...
  Derived(); // cheap constructor
  void initialise(Base& b) { /* implementation goes here */  }
  ...
};

initialise(Base& b) метод извлечет дорогих частей из аргумента. Это может быть разрушительным. База предоставит открытый (или, возможно, защищенный) интерфейс для фактического извлечения.

0 голосов
/ 24 апреля 2011

Как насчет добавления конструктора к Derived?

Derived(Base (*f)(void)) : Base(f()) { ... }

Тогда Derived d3 = getBase; может дать вам необходимую оптимизацию.Вероятно, не очень практично, поскольку мне пришлось указывать пустой список параметров f в Derived, что весьма ограничивает.Но сделайте его конструктором шаблонов, и вы сможете использовать пользовательский функтор, результат лямбды boost:bind или C ++ 0x, если он доступен.

Если это не удастся, извлечь id = ... часть getBase в функцию getId и дать Derived конструктор, принимающий int, который передает id в свой субобъект Base.Без сомнения, реальный код более сложен, чем этот, и это может привести к изменению множества параметров в этом месте.Может быть, легкий BaseParameters класс, который вы используете вместо Base до момента, когда вам действительно понадобится медленная работа, а затем конвертируете , в Base, Derived или другойродственный класс.

...