I'm having this strange problem that I can't wrap my head around. For this code:
struct Foo {
int operator()() const & { return 0; }
double operator()() const && { return 0; }
};
template<typename F>
void test(F&& f)
{
static_assert<is_same<F&&, decltype(f)>::value, "!"); // (1)
// intentionally not forwarding f
using T1 = decltype(f());
using T2 = result_of_t<decltype(f)()>;
using T3 = result_of_t<F&&()>;
using T4 = result_of_t<F&()>;
static_assert(is_same<T1, T2>::value, "!"); // (2)
static_assert(is_same<T1, T3>::value, "!"); // (3)
static_assert(is_same<T1, T4>::value, "!"); // (4)
}
Foo f;
test(f); // all static_asserts passed
test(Foo{}); // (1) and (4) passed, (2) and (3) failed
Since (1) seems to say decltype(f)
is F&&
, I guess (2) and (3) are actually the same. So, how could decltype(f())
and result_of_t<decltype(f)()>
disagree? And why are decltype(f())
and result_of_t<F&()>
the same?
For the test(Foo{})
call decltype(f)
tells you that f
was declared as an rvalue reference type, Foo&&
, but that's the type it was declared with, it doesn't tell you what its value category is (i.e. rvalue or lvalue).
Within the body of the function f
is an lvalue (because it has a name), so decltype(f())
is not the same as result_of_t<F&&()>
Consider:
Foo&& f = Foo{};
f();
Here too, f
is declared as an rvalue reference type, Foo&&
, but that doesn't mean that f()
invokes the &&
-qualified member function. f
is an lvalue, so it invokes the &
-qualified overload. To invoke the &&
-qualified overload you need to use std::move(f)()
to make it an rvalue.
In your test(F&&)
function where you have a universal reference you need to use std::forward
to restore the value category of the incoming argument. To get the same type as result_of_t<decltype(f)()>
you need to forward f
to restore its original value category, e.g.
using T1 = decltype(std::forward<F>(f)());
Now that will have the same type as result_of_t<decltype(f)()>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With