Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use std::result_of_t correctly

Tags:

c++

c++11

c++14

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?

like image 858
Zizheng Tai Avatar asked Jul 12 '16 13:07

Zizheng Tai


1 Answers

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)()>

like image 118
Jonathan Wakely Avatar answered Nov 01 '22 01:11

Jonathan Wakely