Let's look at the excellent-forwarding version:
When called with rvalue, it will return final_action<F>(static_cast<F&&>(f)) .
When called with lvalue, it will return final_action<F&>(f) .
Now consider overloading const F& :
- When the lvalue or rvalue value is called, it will return
final_action<F>(f) .
As you can see, there is an important difference:
live example in wandbox
I'm not sure why const F& overload was deemed necessary.
This is the final_action implementation:
template <class F> class final_action { public: explicit final_action(F f) noexcept : f_(std::move(f)), invoke_(true) {} final_action(final_action&& other) noexcept : f_(std::move(other.f_)), invoke_(other.invoke_) { other.invoke_ = false; } final_action(const final_action&) = delete; final_action& operator=(const final_action&) = delete; ~final_action() noexcept { if (invoke_) f_(); } private: F f_; bool invoke_; };
If I didnโt miss something, creating final_action<F&> does not make sense, since f_(std::move(f)) will not compile.
live example in wandbox
Therefore, I think it should only be:
template <class F> inline final_action<F> finally(F&& f) noexcept { return final_action<std::decay_t<F>>(std::forward<F>(f)); }
Ultimately, I believe that the finally implementation in GSL is incorrect / non-optimal (i.e. redundant, has code repetition).
Vittorio romeo
source share