在必须推导函数调用结果类型的上下文中,C++ 似乎更乐于帮助我们,提供(至少据我所知是以下)两种解决方案:
result of类型特征:
std::result_of<F(Args...)>::type
核心语言语法:
decltype(std::declval<F>()(std::declval<Args>()...);
我的问题是,两者之间有什么区别吗?是否存在一个不能被另一个替代的上下文,如果不是,为什么我们需要一个类型特征来做一些语言可以开箱即用的事情?
最佳答案
存在三个差异。
最初,
std::result_of
不需要对 SFINAE 友好。因此,如果要在上下文中使用它来验证F
可以用Args...
调用,它会给你一个硬错误,而decltype
与std::declval
只会导致可能的预期替代失败。 N3462修复了规范,使其对 SFINAE 友好。虽然在兼容的 C++14 编译器上,两者都是 SFINAE 友好的。
std::result_of_t<Fn(ArgsTypes...)>
实际上是根据后者定义的。来自 [meta.trans.other]:If the expression
INVOKE (declval<Fn>(), declval<ArgTypes>()...)
is well formed when treated as an unevaluated operand (Clause 5), the member typedef type shall name the typedecltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...));
otherwise, there shall be no member type.正如
result_of
的语言定义所明确的那样, 类型Fn
可以是任何东西INVOKE
-able,而显式调用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
对于某些类型,
std::result_of
由于指定某些 type-id 是非法的(参见 my related question ),因此实际上不起作用。这些类型正在使用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.
这会导致 SFINAE 失败(至少这不是硬错误),因为我们必须形成类型
int()()
- 返回函数的空函数。现在,从技术上讲,我们错误地编写了代码。应该是:template <class F, class R = std::result_of_t<F&()>> R call_fixed(F& f) { return f(); }
这行得通。但是如果我们在 declval 上犯了同样的错误,我们会没事的:
template <class F, class R = decltype(std::declval<F>()())> R call_declval(F& f) { return f(); }
关于c++ - 关于获取函数结果类型的各种方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35834026/