Search code examples
c++optimizationassignment-operatorplacement-new

Reducing assignment of temporary object to in-place construction


Using std::list supporting move semantics as an example.

std::list<std::string> X;
... //X is used in various ways
X=std::list<std::string>({"foo","bar","dead","beef"});

The most straightforward way for compiler to do the assignment since C++11 is:

  1. destroy X
  2. construct std::list
  3. move std::list to X

Now, compiler isn't allowed to do following instead:

  1. destroy X
  2. contruct std::list in-place

because while this obviously saves another memcpy it eliminates assignment. What is the convenient way of making second behaviour possible and usable? Is it planned in future versions of C++?

My guess is that C++ still does not offer that except with writing:

X.~X();
new(&X) std::list<std::string>({"foo","bar","dead","beef"});

Am I right?


Solution

  • You can actually do it by defining operator= to take an initializer list. For std::list, just call

        X = {"foo","bar","dead","beef"}.
    

    In your case, what was happening is actually:

    1. Construct a temporary
    2. Call move assignment operator on X with the temporary

    On most objects, such as std::list, this won't actually be expensive compared to simply constructing an object.

    However, it still incurs additional allocations for the internal storage of the second std::list, which could be avoided: we could reuse the internal storage already allocated for X if possible. What is happenning is:

    1. Construct: the temporary allocates some space for the elements
    2. Move: the pointer is moved to X; the space used by X before is freed

    Some objects overload the assignment operator to take an initializer list, and it is the case for std::vector and std::list. Such an operator may use the storage already allocated internally, which is the most effective solution here.

    // Please insert the usual rambling about premature optimization here