正确使用std :: result_of_t

Ziz*_*Tai 5 c++ c++11 c++14

我有这个奇怪的问题,我无法绕过头脑.对于此代码:

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
Run Code Online (Sandbox Code Playgroud)

由于(1)似乎说decltype(f)F&&,我猜(2)和(3)实际上是相同的.那么,怎么可能decltype(f())result_of_t<decltype(f)()>不同意?为什么decltype(f())result_of_t<F&()>相同?

Jon*_*ely 8

因为test(Foo{})调用decltype(f)告诉你f声明为右值引用类型,Foo&&但是它是声明它的类型,它不告诉你它的值类别是什么(即rvalue或lvalue).

在函数体内f是一个左值(因为它有一个名字),所以decltype(f())不一样result_of_t<F&&()>

考虑:

Foo&& f = Foo{};
f();
Run Code Online (Sandbox Code Playgroud)

这里也f被声明为右值引用类型Foo&&,但这并不意味着f()调用&&-qualified成员函数.f是一个左值,所以它调用了&- 限定的重载.要调用&&-qualified重载,您需要使用std::move(f)()它来使其成为右值.

test(F&&)具有通用引用的函数中,您需要使用它std::forward来恢复传入参数的值类别.要获得相同的类型,result_of_t<decltype(f)()>您需要转发f以恢复其原始值类别,例如

using T1 = decltype(std::forward<F>(f)());
Run Code Online (Sandbox Code Playgroud)

现在它将具有相同的类型 result_of_t<decltype(f)()>

  • 是的,但我不会这样说.我会说你应该确保你在`result_of`中使用的类型与调用者的值类别相匹配.如果计划调用左值的调用运算符,请使用带有左值类型的`result_of`.如果计划调用右值的调用运算符,请使用带有右值类型的`result_of`.如果您计划在完美转发对象上调用调用操作符,则使用带有推导类型的`result_of`. (3认同)
  • @jona除非你在`T &&`中推断出`T`,否则你的建议需要解包转发引用的内容以及如何将某些内容声明为右值引用但是是左值.根据Ziz的建议没有.现在,在实践中,有效的groking转发参考可能是有用的,但说"不使用经验法则"有一个陡峭的学习曲线. (2认同)