1

让我们假设我有一堆“未使用”的动态对象。每次我需要一个新对象时,我都会使用其中一个“未使用”对象作为新请愿的占位符。就像是:

template<typename T>
class my_list
{
public:
   template<typename... Args>
   T* newObject(Args&&... args)
   {
       if (unused.empty())
          return new T(forward<Args>(args)...);
       else {
          auto* newbie = unused.top();
          unused.pop();

          newbie = new (newbie) T(forward<Args>(args)...); // l. X

          return newbie;
       }
   }

private:
   stack<T*> unused;

};

问题是,在第 X 行,什么更有效,书面句子,或者:

*newbie = std::move(T(forward<Args>(args)...));

这意味着,哪一个更省时,调用 a newwith newbieas 地址(避免请求新内存)或简单地移动一个新的临时对象来覆盖原始对象?

4

2 回答 2

3

这是另一个可行的选择。它的优点是,虽然完美转发仍然需要 C++11,但它可以使用尚未更新的库代码来实现移动构造和赋值。

T(forward<Args>(args)...).swap(*newbie);

此外,您的移动分配调用已经有一个临时对象,无需显式使其可移动。

*newbie = T(forward<Args>(args)...);

与“先销毁,然后就地构建”的方法相比,这些方法中的任何一个都可以更容易地提供异常安全。

于 2013-03-11T15:05:26.877 回答
2

是不必要的move,因为构造函数调用会临时创建一个纯右值。您的问题归结为哪个更有效:

newbie->~T();
new (newbie) T(std::forward<Args>(args)...);

或者

*newbie = T(std::forward<Args>(args)...);

假设T有一个正确编写的移动赋值运算符,如果有的话,可能几乎没有区别;就副作用而言,T在第一种情况下,在构造函数之前调用析构函数,在第二种情况下调用之后,但没有理由相信这会比另一种更影响性能。后者可能会导致更原始的值副本,但任何体面的优化编译器都会消除它们。

在没有性能测试表明前者显着提高性能的情况下,您应该更喜欢后者,因为它更具可读性,并且更容易使异常安全。

于 2013-03-11T15:05:15.700 回答