【问题标题】:Universal reference l-value not copying object通用参考左值不复制对象
【发布时间】:2020-10-10 07:17:44
【问题描述】:

为什么这些断言适用于以下代码?通用引用应该绑定到左值引用run(T& a),并从a 复制一个对象b。但是,run() 函数中的两个对象地址“a”和“b”是相同的。使用 C++11/14/17/2a gcc-9.2 和 clang++-6.0 测试。标准的哪一部分说这是有效的?没有找到任何相关的东西。

#include <cassert>
#include <utility>

template <typename T>
void run(T&& a)
{
  T b {std::forward<T>(a)};
  ++b;
  assert(b == a);
  assert(&a == &b);
}

int main()
{
  int value {10};
  run(value); // asserts work, not expected
  // run(std::move(value)); // the asserts don't work as expected
}

【问题讨论】:

  • 看到这个link,帮了我很多
  • 有很好的例子,谢谢分享。

标签: c++ c++11 move-semantics lvalue forwarding-reference


【解决方案1】:

但是,run() 函数中的两个对象地址“a”和“b”是相同的。

当被传递一个左值时,Tdeduced 作为左值引用,即int&amp;。 (int&amp; &amp;&amp;collapsesint&amp;,所以函数参数a 的类型是int&amp;。)然后b 被声明为a 的引用绑定。

当被传递一个右值时,T 被推导出为int。 (所以函数参数a的类型是int&amp;&amp;。)然后b被声明为从a复制的自变量。

【讨论】:

  • 这很有趣。我没想到T 的类型会改变,而是参数的类型。语义方面,很明显模板 不是参考。
  • @AdvSphere, &amp;&amp; 是一个右值引用。转发引用(通用引用)并不是真正的自己的实体,只是&amp; &amp;&amp; 变为&amp;&amp;&amp; &amp;&amp;(或&lt;no reference&gt; &amp;&amp;)变为&amp;&amp; 的结果。
  • @AdvSphereT的类型发生变化,导致函数参数类型发生变化。
  • 感谢您的澄清,做了更多的阅读并且有意义。 std::forward 需要包含&amp; 的左值,因此函数的T 类型的整个推导包括引用。
  • @AdvSphere 是的,转发参考和std::forward 必须一起工作;然后我们可以转发保留其值类别的参数。
【解决方案2】:

run(value)中,value是一个左值,需要和T&amp;&amp;匹配。左值不能绑定到右值引用,所以T = intT = int&amp;&amp; 不会这样做,就像T&amp;&amp; = int&amp;&amp; 一样。唯一有效的是T = int&amp;。由于引用折叠,对左值引用的右值引用是左值引用,所以run 的实例化如下:

template<>
void run<int&>(int &a) {
    int &b{a}; // expanding std::forward
    ++b;
    assert(b == a);
    assert(&b == &a);
}

显然,断言总是通过。现在,对于run(std::move(value)),参数确实是一个右值,你得到T = int。那么

template<>
void run<int>(int &&a) {
    int b{std::move(a)};
    ++b;
    assert(b == a);
    assert(&b == &a);
}

这当然失败了。也许你应该更换

T b{std::forward<T>(a)};

std::decay_t<T> b{std::forward<T>(a)};

这将从T 中删除引用(确保b 是一个新的(复制/移动的)对象)并处理数组和函数(通过使b 成为指针,即使a 不是)。

怀疑你需要它们,但是[temp.deduct.call]/3 谈到了转发引用的模板推导,[dcl.init.list]/3.9 说列表初始化引用只是将它绑定到初始化列表的元素。还有[forward],好吧,解释std::forward&lt;T&gt;。基本上,如果T 是一个左值引用,那么std::forward&lt;T&gt;(x) 就是一个左值,否则就是一个xvalue(一种右值)。 (基本上是有条件的std::move。)

【讨论】:

  • 如果您只需要一份副本,auto b = a; 似乎是最直接的选择。这无需明确说明即可衰减。
猜你喜欢
  • 1970-01-01
  • 2011-01-21
  • 2018-08-22
  • 1970-01-01
  • 2017-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-28
相关资源
最近更新 更多