Snippet:
#include <functional>
template <typename T>
struct CallableTrait;
template <typename R, typename... Args>
struct CallableTrait<std::function<R(Args...)>>
{
using ReturnType = R;
};
template <typename Callable>
using CallableTraitT = CallableTrait<decltype(std::function{std::declval<Callable>()})>;
const int foo(const int x)
{
int r = x + 42;
return r;
}
int main()
{
using ReturnT1 = CallableTraitT<decltype(foo)>::ReturnType;
using ReturnT2 = std::invoke_result_t<decltype(foo), decltype(42)>;
static_assert(std::is_same_v<ReturnT1, const int>);
static_assert(std::is_same_v<ReturnT2, int>);
}
Why does std::invoke_result_t
strip off the const
from the foo
return type?
I would like to wrap a callable into another callable with the exact same return type, but apparently I cannot rely std::invoke_result_t
for this.
Because that's how the language works.
A function's return type can be nominally const
-qualified (and that's what you're getting out of CallableTrait
), but the actual result of invoking such a function (i.e. the expression containing the function call) has the const
stripped off if it's a built-in type (for being useless), and that's the thing that invoke_result
is telling you about.
[expr.type/2]
: If a prvalue initially has the type “cvT
”, whereT
is a cv-unqualified non-class, non-array type, the type of the expression is adjusted toT
prior to any further analysis.
Admittedly this is a bit of an oddity, but that's how it is.