О времени стоит, что дороже: движение или перезапись? C++11

Допустим, у меня есть стек "неиспользуемых" динамических объектов. Каждый раз, когда мне нужен новый объект, я использую один из этих "неиспользуемых" объектов в качестве заполнителя для новой петиции. Что-то вроде:

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)...));

Это означает, что то, что более эффективно по времени, - вызов new с newbie как адрес (избегая петиций новой памяти) или просто движение нового временного объекта, перезаписывающего оригинал?

2 ответа

Решение

move не требуется, так как вызов конструктора создает временное значение prvalue. Ваш вопрос сводится к тому, что является более эффективным:

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

или же

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

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

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

Вот еще один жизнеспособный вариант. Преимущество состоит в том, что, хотя C++11 все еще требуется для идеальной пересылки, он работает с библиотечным кодом, который не был обновлен для реализации построения и назначения перемещения.

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

Кроме того, ваш вызов присваивания перемещения уже имеет временный объект, нет необходимости явно делать его подвижным.

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

Любой из них значительно облегчит обеспечение безопасности исключений, чем подход "сначала уничтожить, а затем построить на месте".

Другие вопросы по тегам