Постоянная ссылка на временный объект - PullRequest
0 голосов
/ 27 августа 2009

Допустим, есть такая функция, как

void SendToTheWorld(const Foo& f);

и мне нужно предварительно обработать Foo объект перед отправкой

X PreprocessFoo(const Foo& f)
{
   if (something)
   {
      // create new object (very expensive).
      return Foo();
   }

   // return original object (cannot be copied)
   return f;
}

Использование

Foo foo;
SendToTheWorld(PreprocessFoo(foo));

Таким образом, X PreprocessFoo() функция должна иметь возможность вернуть исходный объект или скопировать / изменить, а затем вернуть новый. Я не могу вернуть const Foo&, так как он может ссылаться на временный объект. Также я не люблю создавать Foo в куче.

Отлично, X должен быть неким объединением const Foo& и Foo, которое можно рассматривать как const Foo&. Есть идеи, как сделать это более элегантно?

Мое текущее решение:

Foo PreprocessFoo(const Foo& f)
{
   // create new object (very expensive).
   return Foo();
}

Использование:

Foo foo;

if (something)
{
   SendToTheWorld(PreprocessFoo(foo));
}
else
{
   SendToTheWorld(foo);
}

Ответы [ 10 ]

2 голосов
/ 27 августа 2009

Я не на 100% понимаю, что вы имеете в виду, но если вы просто хотите опустить ненужную копию Foo в возвращаемом значении, сделайте вашу функцию маленькой (прямо сейчас) и положитесь на оптимизирующий компилятор позаботиться о вашей проблеме.

Как только функция будет встроена, компилятор удалит ненужные копии Foo.

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

1 голос
/ 27 августа 2009

Если foo (в вызывающей функции) больше не используется после вызова, вы можете сделать это с помощью функции swap (специализирующей std::swap для класса Foo, если необходимо):

const Foo& PreprocessFoo(Foo& f)
{
   if (something)
   {
      std::swap(f, Foo());
   }

   // return original or new object
   return f;
}
1 голос
/ 27 августа 2009

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

0 голосов
/ 27 августа 2009

Вот еще одна идея: если условие (something) зафиксировано во время компиляции, вы можете сделать его параметром шаблона в PreprocessFoo(). Затем вы можете использовать TMP для перехода к двум разным функциям с разными типами возврата.

Вот набросок этого:

template< bool Cond >
struct to_the_world_sender;

template<>
struct to_the_world_sender<true> {
  typedef Foo return_type;
  static return_type preprocess(const Foo& foo) {return Foo();}
};

template<>
struct to_the_world_sender<false> {
  typedef const Foo& return_type;
  static return_type preprocess((const Foo& foo) {return foo;}
};

template< typename Cond >
inline 
typename to_the_world_sender<Cond>::return_type PreprocessFoo(const Foo& foo)
{
  return to_the_world_sender<Cond>::preprocess((foo);
}
0 голосов
/ 27 августа 2009

Это не легко.

Я вижу три способа решения этой проблемы, каждый из которых сводится к удешевлению копирования. Все требуют контроля над Foo, также требуется контроль над PreprocessFoo:

  1. Используйте компилятор, реализующий ссылки на rvalue (часть C ++ 1x), и снабдите Foo необходимыми компонентами, чтобы они начали работать и сделали копирование дешевым
  2. Прочитайте статью о моджо Александреску и измените Foo, чтобы реализовать это.
  3. Используйте универсальное лечение программиста: добавьте еще один слой косвенности. Оберните Foo в некоторый интеллектуальный объект, похожий на указатель, что делает копирование дешевым. Это, однако, потребует динамического распределения.

Если вы не можете изменить Foo, тогда я не вижу пути.

0 голосов
/ 27 августа 2009

Ваши требования противоречат друг другу: вы хотите, чтобы объект представлял собой автоматическую переменную, и при этом вы хотите, чтобы его голос определялся его временем жизни. Невозможно вернуть автоматическую переменную по ссылке. Вы можете вернуть его по значению, но это слишком дорого. Моя идея: если стоимость копирования настолько высока, вероятно, стоимость размещения в бесплатном магазине будет небольшой по сравнению с ней.

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

Foo& preProcess( Foo& f ) {
    if( something ) f=Foo(); // hopefully something is special!
    return f;
}

Foo localFoo;
SendToTheWorld( preProcess( localFoo ) );
0 голосов
/ 27 августа 2009

Если время жизни вашего объекта Foo определяется не одним фрагментом кода, почему бы не использовать вместо него shared_ptr?

void SendToTheWorld( shared_ptr<Foo>& pFoo );

shared_ptr<Foo> preProc( shared_ptr<Foo>& pFoo ) {
    if( something ) return new Foo(); // wrapped in a shared pointer
    return pFoo;
}

shared_ptr<Foo> pFoo = new Foo();
SendToTheWorld( preProc( pFoo ) );
0 голосов
/ 27 августа 2009

Что не так с вашим текущим решением? Конструктор копирования для Foo может быть опущен из-за RVO. Ссылка на временный объект будет действовать все время, пока выполняется функция SendToTheWorld.

0 голосов
/ 27 августа 2009
Foo PreprocessFoo(const Foo& f)
{
   if (something)
   {
      // create new object
      return Foo();
   }

   // return original object
   return f;
}
0 голосов
/ 27 августа 2009

Единственный способ, которым я могу думать, - это возвращать объект Foo вместо ссылки.

...