I encountered an issue when trying to perfect forward a function’s result in a generic way.
Here two functions that provide a result:
Foo provideFooAsTemporary()
{
return Foo{};
}
Foo& provideFooAsReference()
{
static Foo foo{};
return foo;
}
Here the erroneous perfect forwarding:
template <typename TFn>
decltype(auto) erroneousForwardFoo(TFn&& fn)
{
auto&& result = fn();
// do some stuff here
return std::forward<decltype(result)>(result);
}
Foo fooTemporary = erroneousForwardFoo(provideFooAsTemporary); // fails
Foo& fooReference = erroneousForwardFoo(provideFooAsReference); // works
When the result is a temporary, the forwarding fails, which is indicated by the result from erroneousForwardFoo
pointing to invalid memory.
In contrast, the following simplified forwarding code works like a charm:
template <typename TFn>
decltype(auto) workingForwardFoo(TFn&& fn)
{
return fn();
}
Foo fooTemporary = workingForwardFoo(provideFooAsTemporary); // works
Foo& fooReference = workingForwardFoo(provideFooAsReference); // works
However, I want to "do some stuff" before returning, so I need a fix for erroneousForwardFoo
.
Debugging has shown that a dangling reference is the problem (see godbolt example). As I understand, this is because the lifetime of the temporary is bound to the rvalue reference result
, and unfortunately moving doesn’t change this.
Now the questions:
erroneousForwardFoo
in a lean way to achieve a correct generic implementation?EDIT 1: Although I only provided a code example for forwarding a lvalue reference, I want the solution to support rvalue references too:
Foo&& provideFooAsRValueReference()
{
static Foo foo{};
return std::move(foo);
}
decltype(auto)
can be used for objects too. I would simply use static_cast
:
decltype(auto) forwarder(auto&& ...fn_or_arg){
decltype(auto) res = std::invoke(std::forward<decltype(fn_or_arg)>(fn_or_arg)...);
/*Manipulate(res)...*/;
return static_cast<decltype(res)>(res);
};
If compiler has difficulty with direct initialization and copy elision, it is possible to return conditionally:
using res_t = decltype(res);
if constexpr (std::is_reference_v<res_t>)
return std::forward<res_t>(res);
else
return res;