关于获取函数结果类型的各种方法

Lor*_*ins 4 c++ type-traits c++14

在必须推导出函数调用结果类型的上下文中,C++似乎更乐意帮助我们,提供(至少据我所知)以下两个解决方案:

我的问题是,这两者之间有什么不同吗?是否存在一个不能被另一个替换的上下文,如果不是为什么我们需要一个类型特征来做一些语言可以开箱即用的东西?

Bar*_*rry 5

有三个不同之处.

  1. 最初,std::result_of不需要SFINAE友好.因此,如果是使用它的上下文来验证F了与调用Args...,它会给你一个严重的错误,而decltypestd::declval只会造成可能的预期替换故障.N3462修正了规格,使其对SFINAE友好.

    但是,在兼容的C++ 14编译器上,两者都是SFINAE友好的.std::result_of_t<Fn(ArgsTypes...)>实际上是根据后者来定义的.来自[meta.trans.other]:

    如果表达式 INVOKE (declval<Fn>(), declval<ArgTypes>()...)在作为未评估的操作数处理时形成良好(第5条),则成员typedef类型应为该类型命名,decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...)); 否则不应有成员类型.

  2. 正如在定义的语言中所明确的那样result_of,类型Fn可以是任何东西INVOKE,而显式调用std::declval<F>()只适用于函数和函数对象.所以如果你有:

    using F = decltype(&X::bar);
    using T1 = std::result_of_t<F(X*)>;                        // ok
    using T2 = decltype(std::declval<F>()(std::declval<X*>()); // not ok
    
    Run Code Online (Sandbox Code Playgroud)
  3. 对于某些类型,std::result_of实际上并不起作用,因为指定某些type-id是非法的(参见我的相关问题).这些类型使用函数F(而不是函数的指针/引用)或使用任何抽象类Args....所以考虑看似无害的东西,如:

    template <class F, class R = std::result_of_t<F()>>
    R call(F& f) { return f(); }
    
    int foo();
    call(foo); // error, unresolved overload etc.
    
    Run Code Online (Sandbox Code Playgroud)

    这使SFINAE失败(至少它不是一个硬错误),因为我们必须形成类型int()()- 返回函数的nullary函数.现在,从技术上讲,我们错误地编写了代码.应该是:

    template <class F, class R = std::result_of_t<F&()>>
    R call_fixed(F& f) { return f(); }
    
    Run Code Online (Sandbox Code Playgroud)

    这有效.但如果我们用declval犯了同样的错误,我们会没事的:

    template <class F, class R = decltype(std::declval<F>()())>
    R call_declval(F& f) { return f(); }
    
    Run Code Online (Sandbox Code Playgroud)