在必须推导出函数调用结果类型的上下文中,C ++似乎更乐意帮助我们,提供(至少据我所知)以下两个解决方案:
result of类型特征:
std::result_of<F(Args...)>::type
核心语言语法:
decltype(std::declval<F>()(std::declval<Args>()...);
我的问题是,这两者之间是否存在差异?是否存在一个不能被另一个替换的上下文,如果不是为什么我们需要一个类型特征来做一些语言可以开箱即用的东西?
答案 0 :(得分:5)
有三个不同之处。
最初,std::result_of
不需要SFINAE友好。因此,如果要在上下文中使用它来验证F
是否可以使用Args...
进行调用,则会给您带来严重错误,而带有decltype
的{{1}}只会导致错误意图替换失败。 N3462修改了规范以使其对SFINAE友好。
在兼容的C ++ 14编译器上,两者都是SFINAE友好的。 std::declval
实际上是根据后者定义的。来自[meta.trans.other]:
如果表达
std::result_of_t<Fn(ArgsTypes...)>
很好 当被视为一个时形成的 未评估的操作数(第5条), 成员typedef类型应命名为 输入INVOKE (declval<Fn>(), declval<ArgTypes>()...)
否则,不得有会员 类型。
正如decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...));
定义的语言所明确的那样,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
实际上并不起作用,因为指定某些 type-id 是非法的(请参阅my related question)。这些类型使用std::result_of
的函数(与函数的指针/引用相对)或使用任何F
的抽象类。所以考虑看似无害的东西,如:
Args...
这使SFINAE失败(至少它不是一个硬错误),因为我们必须形成类型template <class F, class R = std::result_of_t<F()>>
R call(F& f) { return f(); }
int foo();
call(foo); // error, unresolved overload etc.
- 一个返回函数的nullary函数。现在,从技术上讲,我们错误地编写了代码。它应该是:
int()()
这很有效。但如果我们用declval犯了同样的错误,我们就没事了:
template <class F, class R = std::result_of_t<F&()>>
R call_fixed(F& f) { return f(); }
答案 1 :(得分:3)
result_of
在应用于给定类型时会返回std::invoke
的返回类型 - 它甚至在invoke
声明的返回类型中使用。但是,invoke
涵盖的范围远远超过普通的非成员函数调用。您展示的decltype(…)
中的表达式只是result_of
检查的众多表达中的一个。
因此,如果F
保证是函数对象类型,则您的选项只是等效的。否则,result_of<…>
可能在decltype(…)
不存在的情况下有效:
struct A {int i;};
decltype(std::declval<int A::*>()(std::declval<A>())) // error
std::result_of_t<int A::*(A)> // = int&&, treated as a member access
Demo。 (请注意,result_of
从C ++ 14开始是SFINAE友好的,即无效类型会导致decltype
的平滑扣除失败。)
答案 2 :(得分:1)
选择一些经验不足的C ++程序员,让他们猜测每个代码片段的作用。
有多少人获得第一个权利?有多少人得到第二个权利?
赋予此操作一个名称使其更具可读性。
现在接受一些稍微有经验的C ++程序员,并要求他们使用和不使用result_of来获取函数调用的结果类型。有多少让他们正确,需要多长时间?
这已经是非平凡的元编程,并且将其封装起来可以更容易地与Google一起发现并且更容易实现。
此外,第一个版本相当短。
因此result_of非常有用,而且由于标准库本身需要它很多次,所以程序员可以使用它也是微不足道的。