Search code examples
c++perfect-forwardingstdmoveconstruction

C++ - Interaction between move construction and forwarding


If I'm constructing a non-trivial container like a custom map for example, and want to share code between the emplace and insert functions, one approach would be to have the insert function simply call emplace with the supplied element as the argument. eg.:

template<typename... args>
iterator emplace(int id, args &&... params)
{
    // Do complex stuff here, get a location
    *location = value_type(std::forward<args>(params) ...);
}


iterator insert(int id, const value_type &element)
{
    emplace(id, element);
}

Which in the case of insert & should simply call the copy constructor if I understand correctly? However in the case of insert && it becomes a little more confusing:

iterator insert(int id, value_type &&element)
{
    emplace(id, std::move(element));
}

std::move casts element to an xvalue, which is fine - but will std::forward handle this appropriately? ie. for a non-trivial value_type with a non-default move constructor, will the emplace construction line above correctly call the move constructor?


Solution

  • Which in the case of insert & should simply call the copy constructor if I understand correctly?

    Yes, you're giving std::forward a const lvalue in this scenario, and std::forward yields a const lvalue. With that, the copy constructor is called.

    std::move casts element to an xvalue, which is fine - but will std::forward handle this appropriately?

    Yes. std::forward turns rvalues into xvalues and lvalues into lvalues. As long as you don't hand it an lvalue, it won't give you an lvalue. This subsequently means that the move constructor will be called, as intended.

    In args &&... params, args deduces to value_type, making params an rvalue reference when the function template is instantiated. The first overload of std::forward is called, and std::forward<args>(params) yields an xvalue.

    It's actually quite common to implement push_back or insert for containers in terms of emplace_back or emplace.