【问题标题】:Why there are two signatures of std::forward?为什么 std::forward 有两个签名?
【发布时间】:2016-11-17 14:20:00
【问题描述】:

cplusplus.com 所述,std::forward 有两个签名:

template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept;
template <class T> T&& forward (typename remove_reference<T>::type&& arg) noexcept;

std::forward 的典型用途是在将参数传递给其他函数时保留右值性。让我们用一个例子来说明这一点:

void overloaded(int &)  { std::cout << "lvalue"; }
void overloaded(int &&) { std::cout << "rvalue"; }

template <typename T>
void fwd(T && t)
{
  overloaded(std::forward<T>(t));
}

当我们调用fwd(0) 时,T 推导出为intt 的类型为int &amp;&amp;)。然后我们打电话给std::forward&lt;int&gt;(t)。该调用的结果是int &amp;&amp; 类型的表达式,因此选择了overloaded 函数的第二个版本,程序将“rvalue”打印到标准输出。

当我们调用 fwd(i)(其中 i 是一些 int 变量)时,T 推导出为 int&amp;t 的类型为 int &amp;)。然后我们打电话给std::forward&lt;int&amp;&gt;(t)。该调用的结果(在应用引用折叠规则之后)是 int &amp; 类型的表达式,因此选择了 overloaded 函数的第一个版本,程序将“左值”打印到标准输出。

在这两种情况下,我们都使用std::forward 的第一个重载(使用typename remove_reference&lt;T&gt;::type&amp; arg 的那个)。这是因为即使t 的类型是int &amp;&amp;,它也会绑定到左值引用(因为“对某物的右值引用”类型的命名变量本身就是左值,而左值不能绑定到右值引用)。

问题 1:

std::forward 的第二次重载是为了什么?你能想出一些实际的例子,通过右值引用使用arg 的重载吗?

问题 2:

cplusplus.com 说:

两个签名都返回相同的结果:

static_cast<decltype(arg)&&>(arg)

我的问题是,我很确定这是错误的。当我们尝试从std::forward 的第一个重载中返回它时,我们会得到一个编译错误。

fwdint 右值调用时,它用T = int 调用std::forward 的第一个重载。然后,decltype(arg) 将变为int&amp;,因此static_cast&lt;decltype(arg)&amp;&amp;&gt;(arg) 将折叠为static_cast&lt;int&amp;&gt;(arg)。但是返回类型是int &amp;&amp;,我们得到编译错误:

cannot bind ‘std::remove_reference<int>::type {aka int}’ lvalue to ‘int&&’

std::forward 的两个重载版本都应返回 static_cast&lt;T&amp;&amp;&gt;(arg)。我说的对吗?

你认为来自 cplusplus.com 的引用是错误的吗?

【问题讨论】:

  • 第一个问题的答案见stackoverflow.com/questions/38344332/…
  • @TartanLlama 上面有一堆constconst_cast
  • cplusplus.com 不是 cppreferencecppreference.comcppreference。了解其中的区别。
  • @Nawaz 指出差异可能与主题无关(我发现 cppreference 更可靠,我自己)
  • @Yakk 如果您发现错误,您可以随时自行修复:)

标签: c++ c++11 stl rvalue-reference rvalue


【解决方案1】:
class Foo {};

auto f = [](){ return Foo{}; };
auto g = []()->Foo&{ static Foo x; return x; };

template<class T>
std::false_type is_rvalue( T& ) { return {}; }
template<class T>
std::true_type is_rvalue( T&& ) { return {}; }

template<class T, class F>
auto test( F&& f ) {
  return is_rvalue( std::forward<T>( f() ) );
}

int main() {
  std::cout << test<Foo>(f) << "," << test<Foo&>(g) << "\n";
}

有点做作;但是想象一下,您在哪里有一些工厂可能会根据一些不重要的细节生产FooFoo&amp;,但是您知道您想根据第二种类型将其转发T 到@ 987654325@。你知道,如果T 不是引用,那么它不会产生Foo,但如果T 是引用,它可能会。

【讨论】:

    猜你喜欢
    • 2016-11-15
    • 1970-01-01
    • 2014-06-12
    • 2011-07-03
    • 2014-12-20
    • 1970-01-01
    • 2014-10-09
    • 2017-11-05
    • 2017-07-25
    相关资源
    最近更新 更多