Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return by universal reference

std::for_each accepts and returns a functor by value:

template< class InputIt, class UnaryFunction >
UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );

Although the functor can be moved in and moved out, I'm interested whether there can be no object construction involved at all. If I declare my own my_for_each like this:

template< class InputIt, class UnaryFunction >
UnaryFunction&& my_for_each( InputIt first, InputIt last, UnaryFunction&& f);

And inside my_for_each, call f with std::forward<UnaryFunction>(f)(...), I can avoid the cost to move construct the parameter, and as a bonus be able to respect ref-qualifiers. But I'm not really sure what I should return. If I do:

return std::forward<UnaryFunction>(f);

Can bad things (e.g., dangling references) happen?

(This question comes up when I'm designing the for_each in this post.)

like image 982
Zizheng Tai Avatar asked Jul 14 '16 03:07

Zizheng Tai


1 Answers

As the other fine answer noted, passing const& and rvalue reference parameters through is dangerous, as reference lifetime extension does not commute.

The proper thing to return when you want to pass a forwarding reference T&& through is T. This turns an rvalue into a temporary, and leaves lvalues as references.

So:

template< class InputIt, class UnaryFunction >
UnaryFunction my_for_each( InputIt first, InputIt last, UnaryFunction&& f);

Which for temporary f creates a (moved-to) copy.

If you store this copy, it will be elided into the storage, so zero additional cost. Unless you do not store it, and move is expensive, this is basically optimal.

like image 199
Yakk - Adam Nevraumont Avatar answered Sep 21 '22 15:09

Yakk - Adam Nevraumont