说我想要一个通用的高阶函数。最常见的方法是:
auto F1 = [](auto f) { /*...*/ }
template<class F> auto F2(F&& f) { /*...*/ }
我的问题是在高阶函数中如何使用 f
。虽然在A:
std::forward<decltype(f)>(f)(...);
B中至少有两种方式:
std::forward<decltype(f)>(f)(...);
std::forward<F>(f)(...);
这些方式是否相同,如果不是,那么&#34; tie break&#34; ?
答案 0 :(得分:2)
如上所述,案例A和案例B的声明并不相同。情况B可以推导出左值或左值引用,但情况A仅推导出值类型。 auto
与模板参数类似地推断,因此将auto
更改为auto&&
以使声明匹配。
要回答您的问题,他们等同于。唯一的区别是参考折叠发生在案例B中。decltype(f)
将始终是一个引用,而F
将是一个值类型,当传递给F2
的参数是一个右值时:
F2(<lvalue>); decltype(f) == T&, F == T&
F2(<rvalue>); decltype(f) == T&&, F == T
这对std::forward
并不重要,因为参考折叠将始终产生正确的类型。
template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type& t );
如果T
为F&&
,则返回类型仍为T&&
,因为T&& && == T&&
。如果T
只是F
(值类型),则返回类型仍然相同(T && == T&&
)。可以在this site上找到参考折叠规则。
TLDR:没有任何有效差异,因为参考折叠会产生相同的类型。
答案 1 :(得分:2)
让我们来看看当你使用不同类型的参数调用F2
时发生的事情 - 一个右值引用,一个const值左引用和一个非const左值引用。我们不讨论volatile
,因为它基本上没有添加任何新信息(它的行为就像const
或const volatile
)并假设您为了简单起见而传递了不同的int
。
首先,推导出F
的类型:
int&&
,F
将是int
,但是(decltype(f) == int&&
)。const int&
,则F
将为const int&
(decltype(f) == const int&
)int&
,则F
将为int&
(decltype(f) == int&
)(请参阅Scott Meyers的blog,talk或slides获取全面解释。
因此,您可以看到后两种情况,decltype(f) == F
所以没有太多要进一步分析。然而,对于第一种情况它们是不同的,所以让我们深入研究它。
然后,您将F
或decltype(f)
传递给std::forward
作为模板参数。 std::forward
takes in std::remove_reference<T>&
或std::remove_reference<T>&&
,因此参数类型在两种情况下都相同。但是,返回类型可能会有所不同(因为我们为它传递了不同的T
- int
和int&&
)。但是,由于编译器执行的引用折叠(再次参见Scott的详细说明),int&& &&
变为int&&
。所以返回值也是一样的。
总而言之,如果您对decltype(f)
使用F
或std::forward
,则没有区别。
此外,这里有一些live示例,其中显示了所有推断出的类型,即使对于像const volatile int &&
这样无用(但有效)的野兽也是如此。