Understanding the need for this `const &` specialization

There is a class in the manual support library called final_action (essentially known as ScopeGuard). To generate this template class, there are 2 autonomous convenience functions:

 // finally() - convenience function to generate a final_action template <class F> inline final_action<F> finally(const F& f) noexcept { return final_action<F>(f); } template <class F> inline final_action<F> finally(F&& f) noexcept { return final_action<F>(std::forward<F>(f)); } 

(source: https://github.com/Microsoft/GSL/blob/64a7dae4c6fb218a23b3d48db0eec56a3c4d5234/include/gsl/gsl_util#L71-L82 )

What is the need for the first? If we had only the second one (using redirects, aka universal links), would it not do the same?

+7
c ++ reference overloading c ++ 11 guideline-support-library
source share
1 answer

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:

  • Passing a const lvalue reference to finally will create a wrapper that stores F&

  • Passing a const lvalue reference to finally will create a shell in which F is stored

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).

+7
source share

All Articles