At https://en.cppreference.com/w/cpp/utility/functional/function_ref/function_ref.html, there is an overloaded ctor as follows:
template<class F>
function_ref(F&& f) noexcept;
- Initializes bound-entity with
std::addressof(f)
, and thunk-ptr with the address of a function thunk.
- Let
T
bestd::remove_reference_t<F>
. This overload participates in overload resolution only if :
std::remove_cvref_t<F>
is not the same type asfunction_ref
,std::is_member_pointer_v<T>
isfalse
, and/\*is-invocable-using\*/</\*cv\*/ T&>
istrue
.
It seems that the ctor allows the caller to pass in an expiring functor and uses the dangling reference to invoke the DEAD functor. The code below can demonstrate the issue.
#include <functional>
std::function_ref<int(int)> f() {
auto n = 1;
auto const fn = [n](int m) { return n + m; };
return std::function_ref<int(int)>(std::move(fn)); // eligible for ctor 2.
}
int main() {
auto fn_ref = f();
// fn_ref is now referencing to a dead functor!
return fn_ref(2); // BANG!
}
Compare it with std::ref
and std::cref
which are well designed for the same issue:
template<class T>
void ref(const T&&) = delete;
template<class T>
void cref(const T&&) = delete;
I just wonder:
Why does the standard not just reject the functor argument if it is an rvalue-reference (including &&
and const&&
)?
The primary reason that std::function_ref
was added was to be used as a function argument[1]:
extern int do_something(std::function_ref<int(int)>);
In this case, it entirely makes sense to pass just a reference to a temporary. Any expression involving a call to do_something
will extend the lifetime of any temporary used to construct the function_ref
to at least until the function call ends, so the reference will be valid:
int x = do_something([n](int m) { return n + m; });
// function_ref holds a rerference to a temporary with closure type whose lifetime ends after x is initialised, after `do_something` returns.
This exact same foot-gun manifests with const
references and std::initializer_list
. As with initializer_list
, just don't use function_ref
unless it's the exact situation of using it for a parameter that you don't store beyond the scope of the function.