Return to universal link

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

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

Despite the fact that the functor can be moved and moved, I am interested in whether there can be any objects at all. If I declare my own my_for_each as follows:

 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 of moving to construct the parameter and, as a bonus, be able to respect ref qualifiers. But I'm not quite sure what I should return. If I do this:

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

Are bad things possible (like dangling links)?

(This question comes up when I design for_each in this post .)

+5
source share
2 answers

As noted in another excellent answer, passing const& and rvalue reference parameters is dangerous because the reference lifetime does not commute.

The right thing to return when you want to pass the link to forward T&& through is T This turns rvalue into a temporary value and leaves lvalues ​​as references.

So:

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

What for temporary f creates a copy (moved).

If you save this copy, it will be deleted in the repository, so there is zero extra cost. If you do not store it, and the traffic is expensive, it is basically optimal.

+8
source

Bad things can and will happen.

 struct my_functor { void operator()(int x) { sum += x; } int sum = 0; } std::vector<int> v{1,2,3}; const auto& s = my_for_each(v.begin(), v.end(), my_functor{}); auto sum = s.sum; 

This code is undefined. You are creating a temporary instance of my_functor . This is associated with a rvalue reference, extending its lifetime until it receives a call to the my_for_each function. When the function exits, it will return the rvalue reference to a temporary one. However, when the function exits, the rvalue reference that is temporarily initially associated with it will be destroyed because it is local. At this point, the temporary part will be destroyed. The net effect of all this is that s is a link right away.

Note that with the original for_each functor would be returned with a value, and the link would for_each to it directly, extending its lifetime.

Herb Sutter talks about this in this cppcon talk: https://www.youtube.com/watch?v=hEx5DNLWGgA . Basically, when you return a link / pointer to a function, there are very few reliable options that it can actually reference. The arguments to the function, which are taken by reference, are one of them, but the arguments to the functions from const ref and rvalue ref are not related to the fact that they attract temporary ones and cause the function to return to hang. I wrote about this topic here: http://www.nirfriedman.com/2016/01/18/writing-good-cpp-by-default-in-the-stl/ .

If you want to take a functor and avoid building an object, I just take it by translating the link and I won’t return anything. What for? Because, if subsequently the user does not need a functor, he can just pass the rvalue and not worry about using the return incorrectly. And if they need it, they can just build it, pass it as an lvalue, and then use it after.

 template< class InputIt, class UnaryFunction > void my_for_each( InputIt first, InputIt last, UnaryFunction&& f); my_functor s{}; my_for_each(v.begin(), v.end(), s); auto sum = s.sum; 

No construction / demolition, no problems. Although I would caution that if you try to avoid this for performance reasons, it is probably wrong if your functor / lambda is not big, which is unusual.

+4
source

All Articles