什么时候 forward<T> 等同于 forward<decltype(Targ)> ?
When Is forward<T> equivalent to forward<decltype(Targ)> ?
假设我想要一个通用的高阶函数。最常见的方法是:
一个。通用 lambda
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)(...);
这些方法是否等效?如果不是,"tie breaking" 的一些示例是什么?
正如所写,案例 A 和案例 B 的声明不同。案例 B 可以推导出左值或右值引用,但案例 A 只能推导出值类型。 auto
的推导类似于模板参数,因此将 auto
更改为 auto&&
以使声明匹配。
为了回答您的问题,它们 是 等效的。唯一的区别是引用折叠发生在情况 B 中。 decltype(f)
将始终是引用,而当传递给 F2
的参数是右值时 F
将是值类型:
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&&
,则 return 类型仍然是 T&&
,因为 T&& && == T&&
。如果 T
只是一个 F
(值类型),那么 return 类型仍然相同(T && == T&&
)。可以在 this site.
上找到参考折叠规则
TLDR:没有有效差异,因为引用折叠产生相同的类型。
让我们来看看当您使用不同类型的参数调用 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 or slides 以获得全面的解释)。
正如您所看到的后两种情况,decltype(f) == F
因此没有太多需要进一步分析的地方。但是,对于第一种情况,它们是不同的,所以让我们更深入地研究一下。
然后将 F
或 decltype(f)
作为模板参数传递给 std::forward
。 std::forward
takes in a std::remove_reference<T>&
或 std::remove_reference<T>&&
因此两种情况下的参数类型相同。但是,return 类型可能会有所不同(因为我们为其传递了不同的 T
- int
和 int&&
)。然而,由于编译器执行的引用折叠(再次,请参阅 Scott 的详细解释),int&& &&
简单地变为 int&&
。所以 return 值也是一样的。
总而言之,对于 std::forward
使用 decltype(f)
或 F
没有区别。
此外,这里有一些 live 示例,它显示了所有推导的类型,即使对于像 const volatile int &&
.
这样无用(但有效)的野兽也是如此
假设我想要一个通用的高阶函数。最常见的方法是:
一个。通用 lambda
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)(...);
这些方法是否等效?如果不是,"tie breaking" 的一些示例是什么?
正如所写,案例 A 和案例 B 的声明不同。案例 B 可以推导出左值或右值引用,但案例 A 只能推导出值类型。 auto
的推导类似于模板参数,因此将 auto
更改为 auto&&
以使声明匹配。
为了回答您的问题,它们 是 等效的。唯一的区别是引用折叠发生在情况 B 中。 decltype(f)
将始终是引用,而当传递给 F2
的参数是右值时 F
将是值类型:
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&&
,则 return 类型仍然是 T&&
,因为 T&& && == T&&
。如果 T
只是一个 F
(值类型),那么 return 类型仍然相同(T && == T&&
)。可以在 this site.
TLDR:没有有效差异,因为引用折叠产生相同的类型。
让我们来看看当您使用不同类型的参数调用 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 or slides 以获得全面的解释)。
正如您所看到的后两种情况,decltype(f) == F
因此没有太多需要进一步分析的地方。但是,对于第一种情况,它们是不同的,所以让我们更深入地研究一下。
然后将 F
或 decltype(f)
作为模板参数传递给 std::forward
。 std::forward
takes in a std::remove_reference<T>&
或 std::remove_reference<T>&&
因此两种情况下的参数类型相同。但是,return 类型可能会有所不同(因为我们为其传递了不同的 T
- int
和 int&&
)。然而,由于编译器执行的引用折叠(再次,请参阅 Scott 的详细解释),int&& &&
简单地变为 int&&
。所以 return 值也是一样的。
总而言之,对于 std::forward
使用 decltype(f)
或 F
没有区别。
此外,这里有一些 live 示例,它显示了所有推导的类型,即使对于像 const volatile int &&
.