I am in the middle of writing some generic code for a future library. I came across the following problem inside a template function. Consider the code below:
In case you need to use result (in non-void cases) in "some generic stuff", I propose a if constexpr based solution (so, unfortunately, not before C++17).
Not really elegant, to be honest.
First of all, detect the "true return type" of f (given the arguments)
using TR_t = std::invoke_result_t;
Next a constexpr variable to see if the returned type is void (just to simplify a little the following code)
constexpr bool isVoidTR { std::is_same_v };
Now we define a (potentially) "fake return type": int when the true return type is void, the TR_t otherwise
using FR_t = std::conditional_t;
Then we define the a smart pointer to the result value as pointer to the "fake return type" (so int in void case)
std::unique_ptr pResult;
Passing through a pointer, instead of a simple variable of type "fake return type", we can operate also when TR_t isn't default constructible or not assignable (limits, pointed by Barry (thanks), of the first version of this answer).
Now, using if constexpr, the two case to exec f (this, IMHO, is the ugliest part because we have to write two times the same f invocation)
if constexpr ( isVoidTR )
std::forward(f)(std::forward(args)...);
else
pResult.reset(new TR_t{std::forward(f)(std::forward(args)...)});
After this, the "some generic stuff" that can use result (in non-void cases) and also `isVoidTR).
To conclude, another if constexpr
if constexpr ( isVoidTR )
return;
else
return *pResult;
As pointed by Barry, this solution has some important downsides because (not void cases)
returnTR_t (the type returned by f()) is a reference typeAnyway, the following is a full compiling C++17 example
#include
#include
template
auto foo (F && f, As && ... args)
{
// true return type
using TR_t = std::invoke_result_t;
constexpr bool isVoidTR { std::is_same_v };
// (possibly) fake return type
using FR_t = std::conditional_t;
std::unique_ptr pResult;
if constexpr ( isVoidTR )
std::forward(f)(std::forward(args)...);
else
pResult.reset(new TR_t{std::forward(f)(std::forward(args)...)});
// some generic stuff (potentially depending from result,
// in non-void cases)
if constexpr ( isVoidTR )
return;
else
return *pResult;
}
int main ()
{
foo([](){});
//auto a { foo([](){}) }; // compilation error: foo() is void
auto b { foo([](auto a0, auto...){ return a0; }, 1, 2l, 3ll) };
static_assert( std::is_same_v );
}