当分配给不同类型时,返回值优化是否有效?

发布于 2024-11-02 23:54:24 字数 1549 浏览 0 评论 0原文

考虑以下两个类:

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可以通过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()

但是,当 main 中需要 Derived 对象时,这里没有任何好处。 “向上转换”需要另一个构造函数/析构函数对。

情况2中,返回值优化不起作用。这里创建和销毁两个对象:

  1. Base(int) //创建临时
  2. ~Base() //销毁临时
  3. Base(const Base&) //via Derived(const Base&)
  4. ~Base() //via ~Derived()

现在在我看来,我有三个相互冲突的要求:

  1. 我想避免以下开销创建临时对象(因为对象在Base类中创建和销毁相当昂贵)
  2. main中,我需要一个派生对象而不是Base >-要使用的对象。

显然,天下没有免费的午餐。但我可能错过了一些东西。所以我的问题是:有没有办法结合这些要求?或者有人有过类似的经历吗?

旁注:我知道“upcast” Derived(const Base& other) 在运行时可能会失败(这已得到解决)。由于代码在语法层面上没有问题,我猜这不是编译器避免 RVO 的原因。

Consider the following two classes:

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() {}
  ...
};

This means Base can be "upcast" by means of the second constructor of Derived.
Now consider the following code:

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

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

I am using VS2008 with optimizations turned on (/Ox /Ob2 /Oi /Ot). I checked calls to constructors and destructors on the console-output:

In Case 1 return value optimization works. There are two calls to:

  1. Base(int)
  2. ~Base()

However, there is nothing to be won here, when a Derived-object is needed in main. The "upcast" requires another constructor/destructor pair.

In Case 2 return value optimization does not work. Two objects are created and destroyed here:

  1. Base(int) //Create temporary
  2. ~Base() //Destroy temporary
  3. Base(const Base&) //via Derived(const Base&)
  4. ~Base() //via ~Derived()

Now it seems to me, that I have three conflicting requirements:

  1. I'd like to avoid the overhead of creating the temporary object (because object creation and destruction is rather expensive in class Base)
  2. In main, I need a Derived-object instead of a Base-object to work with.

Obviously, there's no free lunch here. But I might have missed something. So my question is: Is there a way to combine these requirements? Or has anyone had similar experiences?

Sidenote: I am aware of the fact, that the "upcast" Derived(const Base& other) might fail during runtime (this has been taken care of). Since the code is ok on syntactic level, I'd guess this not the reason for the compiler to avoid RVO.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

完美的未来在梦里 2024-11-09 23:54:24

这很糟糕。

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) 方法将从参数中提取昂贵部分。它可能具有破坏性。 Base 将提供公共(或可能受保护)接口来进行实际提取。

This is bad.

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

The static type of other can belong to the derived type. In that case it will get sliced. On top of that base class would be copied anyway.

RVO is about bypassing copy constructor and initialising the returned object in-place. If you need an object of the derived type you would have to to construct it first. RVO can't construct it for you.

Instead of Derived(const Base& other) you may want to consider different approach. How about this:

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) method would extract expensive parts from the argument. It can be destructive. Base will provide public (or maybe protected) interface to do the actual extraction.

烏雲後面有陽光 2024-11-09 23:54:24

Derived 添加一个构造函数怎么样?

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

然后 Derived d3 = getBase; 可能会得到您想要的优化。可能不太实用,因为我必须在 Derived 中指定 f 的空参数列表,这是相当有限的。但将其设为模板构造函数,您就可以使用用户编写的函子、boost:bind 的结果或 C++0x lambda(如果可用)。

如果失败,请将 getBaseid = ... 部分提取到函数 getId 中,并为 Derived 提供一个构造函数采用 intid 传递给其 Base 子对象。但毫无疑问,真实的代码比这更复杂,这可能会导致拖拽大量有关该位置的参数。也许是一个轻量级 BaseParameters 类,您可以使用它来代替 Base,直到您真正需要完成缓慢的工作,然后将其转换BaseDerived 或其他相关类。

How about adding a constructor to Derived?

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

Then Derived d3 = getBase; might get you the optimization you want. Probably not very practical as it is, since I've had to specify the empty parameter list of f in Derived, which is quite limiting. But make it a template constructor and you can use a user-written functor, the result of a boost:bind or a C++0x lambda where available.

Failing that, extract the id = ... part of getBase into a function getId, and give Derived a constructor taking int that passes the id on to its Base sub-object. No doubt the real code is more complex than this, though, and that might result in hauling a lot of parameters about the place. Maybe a lightweight BaseParameters class, which you use in place of Base until the point where you actually need the slow work done, and then convert that to Base, Derived, or another related class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文