【问题标题】:r-value causes a warning without the use of std::mover-value 在不使用 std::move 的情况下导致警告
【发布时间】:2017-04-02 08:58:33
【问题描述】:

谁能帮我理解为什么下面的代码会导致警告

struct A
{
  A() : _a( 0 ) {}

  const int& _a;
};


int main()
{
  A a;
}

有警告

warning: binding reference member '_a' to a temporary value [-Wdangling-field]
      A() : _a( 0 ) {}

但这段代码(其中std::move 用于初始化成员_a)没有:

struct A
{
  A() : _a( std::move(0) ) {}

  const int& _a;
};


int main()
{
  A a;
}

0 和 std::move( 0 ) 不都是 r 值吗?

【问题讨论】:

  • 这里的_a指的是哪里?
  • _a 是一个引用,在 ex1 中你将它绑定到一个临时的 (0),稍后使用它是 UB。在 ex2 中,您使用 std::move 作为对编译器“撒谎”的强制转换,它使警告静音,但稍后访问它仍然是 UB。
  • const & 在类中使用时不会延长生命周期。它仅适用于函数参数和函数返回。
  • @NathanOliver 我认为您将其与其他内容混为一谈。 foo(bar{}); 不会改变 bar 跨越完整表达式的生命周期,另一方面 bar const &b = bar{}; 确实延长了 bar 的生命周期,而没有看到函数。
  • @Quentin 是的,还有其他几个案例。我的主要观点是,您不能通过 const & 将临时值传递给类并让类延长生命周期。

标签: c++ c++14 move-semantics rvalue-reference


【解决方案1】:

这是一个表达式:

0

这是一个非常小的表达,真的。但它是一种表达方式。

一旦对表达式求值,它就会消失。它消失了。加入合唱团隐形。去见它的制造者。它变成了前表达。

确实,将const 引用绑定到临时值会将临时值的范围扩展到封闭范围的末尾。

但在这种情况下,表达式的范围是构造函数。构造函数完成后,临时值被销毁。

您的编译器注意到,作为类成员,对表达式的const 引用仍然继续存在。您的编译器建议您现在使用类成员将导致未定义的行为。你的编译器想成为你的朋友。您的编译器不希望您编写有缺陷的代码,因此您可以从您的编译器那里获得一些免费、友好的建议。

在另一种情况下,您添加了一些稍微复杂的附加代码。它仍然是未定义的行为,但代码现在足够复杂,编译器无法看到未定义的行为结果。但它仍然是同样的错误。

当编译器发现潜在问题时,编译器会尝试警告您。不幸的是,编译器不能每次都找到所有可能的潜在问题。但是,当它很明显时,编译器会通知您。

【讨论】:

  • 绝对散文。
  • 你确定吗?如果是这样的话,人们将永远无法拥有智能指针。
【解决方案2】:

它们的返回值并不完全相同。 来自cppreference.com

特别是,std::move 会生成一个 xvalue 表达式,用于标识 它的论点 t。它完全等同于将 static_cast 转换为 rvalue 参考类型。

现在,查看右值引用,我们看到第二个示例中的对象“0”可以活得更久:

右值可用于初始化右值引用,在这种情况下 由右值标识的对象的生命周期延长到 参考范围结束。

此类引用(右值引用)随后被分配给类成员 _a,这是允许的,因此您没有错误。

此外,对临时的右值引用可以在移动构造函数中使用,所以如果你正在初始化的成员有它,我看不出问题。但是,在 C++ 中,您永远无法知道未定义的行为何时会突然袭击您 :)

【讨论】:

  • Cppreference 解释了这一点,它解释了生命周期的延长。现在从你引用的句子链接。
  • @Cubby,抱歉,现在链接了什么?要我链接一些东西吗?
  • cppreference 上那句话中的“扩展”一词现在可以点击,并指向解释问题的页面。
猜你喜欢
  • 2022-01-05
  • 2023-03-06
  • 2014-07-29
  • 2020-06-15
  • 1970-01-01
  • 1970-01-01
  • 2017-02-14
  • 2021-11-22
相关资源
最近更新 更多