【发布时间】:2019-04-14 22:01:50
【问题描述】:
当给出以下结构的代码时
template <typename... Args>
void foo(Args&&... args) { ... }
我经常看到库代码在函数中使用static_cast<Args&&> 进行参数转发。通常,这样做的理由是使用static_cast 可以避免不必要的模板实例化。
给定语言的引用折叠和模板推导规则。我们通过static_cast<Args&&> 获得了完美的转发,此声明的证据如下(在误差范围内,我希望答案能启发)
- 当给定右值引用时(或为了完整性 - 没有像this example 中的引用限定),这会以结果为右值的方式折叠引用。使用的规则是
&& &&->&&(上面的规则1) - 当给定左值引用时,这会折叠引用以使结果是左值。这里使用的规则是
& &&->&(上面的规则2)
这实质上是让foo() 将参数转发到上面示例中的bar()。这也是您在此处使用std::forward<Args> 时会得到的行为。
问题 - 为什么在这些情况下使用 std::forward?避免额外的实例化是否有理由打破约定?
Howard Hinnant 的论文 n2951 指定了 6 个约束条件,std::forward 的任何实现都应该“正确”地运行。这些是
- 应该将左值作为左值转发
- 应该将右值作为右值转发
- 不应将右值作为左值转发
- 应将较少的 cv 限定表达式转发到更多 cv 限定的表达式
- 应将派生类型的表达式转发到可访问、明确的基类型
- 不应转发任意类型转换
(1) 和 (2) 被证明可以与上面的 static_cast<Args&&> 一起正常工作。 (3) - (6) 在这里不适用,因为在推导的上下文中调用函数时,这些都不会发生。
注意:我个人更喜欢使用std::forward,但我的理由纯粹是我更喜欢遵守约定。
【问题讨论】:
-
我认为在某些模板库中,人们避免使用
std::forward,因为他们希望库是独立的,并且不想使用标准库。他们还将实现自己的std::move等。 -
@VTT 在这种情况下不是,因为左值通常被推导出为
T&- wandbox.org/permlink/hPucHiFB2pwh53Ox -
std::forward和std::move是为了便于阅读,您可以使用static_cast来获得相同的行为 -
MM 说了什么。在这个answer 中,霍华德提到
std::move的引入是为了使提案更受欢迎。然后就卡住了。我想std::forward也是如此。 -
@M.M 您关于可读性的观点适用。然而,std::move 在我看来略有不同,因为我没有看到它的实现会以它的方式来防止用户错误,使它成为一个更简单的 1-1 映射映射。 std::forward 通常有两个重载,大概是为了安全,这一事实让我认为这种情况可能更复杂。虽然,我没有充分的理由证明这一点。因此,我猜这个问题。
标签: c++ templates c++17 rvalue-reference forwarding