当前进<t>等于前进<decltype(targ)>?

时间:2015-10-18 01:01:16

标签: c++ c++14 perfect-forwarding

说我想要一个通用的高阶函数。最常见的方法是:

甲。通用lambda

auto F1 = [](auto f) { /*...*/ }

B中。功能模板

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; ?

2 个答案:

答案 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 );

如果TF&&,则返回类型仍为T&&,因为T&& && == T&&。如果T只是F(值类型),则返回类型仍然相同(T && == T&&)。可以在this site上找到参考折叠规则。

TLDR:没有任何有效差异,因为参考折叠会产生相同的类型。

答案 1 :(得分:2)

让我们来看看当你使用不同类型的参数调用F2时发生的事情 - 一个右值引用,一个const值左引用和一个非const左值引用。我们不讨论volatile,因为它基本上没有添加任何新信息(它的行为就像constconst 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的blogtalkslides获取全面解释。

因此,您可以看到后两种情况,decltype(f) == F所以没有太多要进一步分析。然而,对于第一种情况它们是不同的,所以让我们深入研究它。

然后,您将Fdecltype(f)传递给std::forward作为模板参数。 std::forward takes in std::remove_reference<T>&std::remove_reference<T>&&,因此参数类型在两种情况下都相同。但是,返回类型可能会有所不同(因为我们为它传递了不同的T - intint&&)。但是,由于编译器执行的引用折叠(再次参见Scott的详细说明),int&& &&变为int&&。所以返回值也是一样的。

总而言之,如果您对decltype(f)使用Fstd::forward,则没有区别。

此外,这里有一些live示例,其中显示了所有推断出的类型,即使对于像const volatile int &&这样无用(但有效)的野兽也是如此。