在什么情况下,std::forward<X> 和 static_cast<X&&> 是等效的?

4

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0644r1.html的文件表示:

There are two ways that you can forward a variable: you can use std::forward or you can use static_cast directly (as this proposal's forwarding operator does):

   template <class X, class Y>
   decltype(auto) foo(X&& x, Y&& y) {
       return std::forward<X>(x)(std::forward<Y>(y));    // with std::forward
       return static_cast<X&&>(x)(static_cast<Y&&>(y));  // with static_cast, exactly equivalent
   }
“完全等效”适用的原因是因为XY是模板参数,所以X&&Y&&是转发引用吗?还是其他原因?
我认为它并不总是适用,因为如果是这样:
  1. 我会期望 std::forward 的文档中也有说明,就像 std::move 的文档中一样;
  2. 用一个替换另一个为什么会导致14%的编译速度提升呢。
目前我最好的猜测是:
  1. std::forward 在某些情况下无法编译,而static_cast可以;
  2. 但反过来不一定成立:如果std::forward可以编译,那么static_cast也可以;
  3. 如果两者都可以编译,结果是相同的。
但我远不确定这是正确的。

1
你猜的2和3是错误的:https://godbolt.org/z/nE6xad3hq - Artyer
@DrewDormann 不,我不是这个意思。我知道它们在转发引用方面是等效的(尽管在重新阅读时,我没有表达得足够清楚),我想知道它们在其他情况下是否也是等效的。 - Alexey Romanov
@AlexeyRomanov:那些“其他情况”很复杂,因为我们不仅要考虑各种类型的X,还要考虑参数/操作数的类型。除了你想要考虑的示例中的关系之外,它们之间是否存在其他关系,或者你真的在询问所有可能性? - Davis Herring
@DavisHerring 我对一般情况很感兴趣,但任何特定的差异(比如Artyer的例子)也是不错的。 - Alexey Romanov
1个回答

0
为了真正地泛化,我们必须看一下 static_cast<X&&>(…)std::forward<X>(…) 的定义。请注意,我们可以忽略 X 不是引用类型的情况,因为通过引用折叠,它最终相当于相应的右值引用类型。所以让 XY&Z&&,其中 YZ 不是引用类型。
在左值引用情况下,相关的 static_cast 能力为(其中 BY 或其基类):
  1. B&Y&
  2. Y &x(…);
对于 std::forward,实例化声明如下:
constexpr Y& forward(Y&) noexcept;
constexpr Y& forward(Y&&) noexcept;  // definition ill-formed

在右值引用的情况下,它们是(其中另外 DZ 或其派生类):
  1. B&&Z&&
  2. D&Z&&
  3. Z &&x(…);
并且。
constexpr Z&& forward(Z&) noexcept;
constexpr Z&& forward(Z&&) noexcept;

std::forward 的引用参数是从参数进行复制初始化,而 static_cast 可以执行以下三种操作之一:

  1. 直接从其操作数进行初始化,这会在涉及显式转换函数时导致重载分辨率差异(如 Artyer 所指出的),
  2. 向下转型,使得 static_cast<Derived&&>(Base()) 起作用,而 std::forward<Derived>(Base()) 不能隐式转换参数,或者
  3. std::forward 本身的实现,它(正如人们所希望的!)不会产生任何区别,因为它仅适用于 rvalue 情况,在此情况下,std::forward 的两个重载中的任何一个都将等效地接受任何派生类型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接