Search code examples
c++move-semanticsperfect-forwarding

push object to vector after passing through conversion function


I desire to insert a conversion function (probably a std::function) to modify an object that is being pushed onto a std::vector. I would like the object to construct in-place, and pass through the function without additional copies being constructed.

Let's say I'm doing this:

std::vector<foo> foo_array;

foo_array.push_back(foo(foo_construct_arguments...));

Now I want to do this:

foo_array.push_back(convert(foo(foo_construct_arguments...)));

Where 'convert' is something like this:

std::function<foo &(foo&)> convert{ [&](foo &f)->foo& { return f; } }

Obviously, the function must modify f to be useful, but in the simplest case let's say it just returns the object.

I want to end up with syntax that looks something like this:

foo_array.emplace_back(convert(foo(foo_construct_arguments...)));

And have an instance of foo constructed, modified by the convert function, then emplaced in the vector with only a single construction. It seems like there should be some way of specifying r-value references in the proper places to get this to happen, but I'm not familiar enough with move-semantics etc. to make it happen. I can find ways to get there with 1 copy along the way.


Solution

  • The syntax needed here is:

    std::function<foo&&(foo&&)> convert{[](foo&& f) -> foo&& { return std::move(f); } };
    

    Or, fully generic lambda fn:

    auto fn = [](auto&& f) -> decltype(auto) { return std::forward<decltype(f)>(f); };
    std::function<foo&&(foo&&)> convert{fn};
    

    If this code is on a hot path, you may like to avoid wrapping a lambda into std::function, if possible.


    Alternatively, emplace_back returns a reference to the new element, which you can then use to modify the element in-place:

    std::vector<foo> foo_array;
    std::function<void(foo&)> convert{...};
    convert(foo_array.emplace_back(foo_construct_arguments...));