对临时对象的常量引用

时间:2009-08-27 07:36:15

标签: c++ implementation idioms

假设有一个像

这样的函数
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 个答案:

答案 0 :(得分:2)

我不是100%清楚你的意思,但是如果你只是想在返回值中省略不必要的Foo副本,那么让你的函数变小(现在,它是)并依赖于优化编译器以处理您的问题。

一旦内联函数,编译器将忽略Foo

的不必要副本

(注意感兴趣:NRVO(命名返回值优化)不能在此处应用,因为不能为可能的返回值的所有实例分配唯一名称。)

答案 1 :(得分:1)

如果你不想在堆中创建对象,那么你应该在调用函数之前创建它并在那里传递一个引用。想想临时性的本质:函数在堆栈中创建新对象,然后返回,删除其堆栈帧。所以对象应该在那里,或者应该在那里复制。

答案 2 :(得分:1)

如果调用后foo(在调用函数中)没有再次使用,您可以使用swap函数(专门为std::swap进行类{{1}如有必要):

Foo

答案 3 :(得分:0)

我能想到的唯一方法是返回一个Foo对象而不是一个引用。

答案 4 :(得分:0)

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

   // return original object
   return f;
}

答案 5 :(得分:0)

您当前的解决方案有什么问题?由于RVO,Foo的复制构造函数可以省略。在执行函数SendToTheWorld时,对临时对象的引用将始终有效。

答案 6 :(得分:0)

如果您的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 ) );

答案 7 :(得分:0)

您的要求存在冲突:您希望对象成为自动变量,但您希望对其生命周期发表意见。通过引用返回自动变量是不可能的。你可以按价值归还,但这太贵了。我的想法是:如果复制成本如此之高,那么分配给免费商店的成本可能比较小。

您可以使用输出参数,大部分时间不能复制原始参数:

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

Foo localFoo;
SendToTheWorld( preProcess( localFoo ) );

答案 8 :(得分:0)

这不是一件容易的事。

我看到了解决这个问题的三种方法,所有这些都归结为使复制更便宜。所有这些都需要控制Foo,还需要控制PreprocessFoo

  1. 使用一个实现右值引用的编译器(C ++ 1x的一部分)并为Foo装备必要的东西,让它们启动并使复制变得便宜。
  2. 阅读Alexandrescu's mojo article并更改Foo以实现该目标。
  3. 使用Programmer的Universal Cure:添加另一层间接。在一些类似智能指针的对象中包裹Foo,使复制变得便宜。但是,这需要动态分配。
  4. 如果您无法更改Foo,那么我看不到任何方法。

答案 9 :(得分:0)

以下是另一个想法:如果条件(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);
}