理解警告:将r值绑定到l值引用

Understanding the warning: binding r-value to l-value reference

本文关键字:绑定 引用 警告      更新时间:2023-10-16

我想通过引用传递一个结构,这样它就不会被复制,但Resharper给出了以下警告:

struct sometype {
};
sometype foo() {
    sometype x;
    return x;
}
void bar() {
    sometype & a = foo();//Binding r-value to l-value reference is non-standard Microsoft C++ extension
    sometype && b = foo(); //ok
}

问题:

sometype & a = foo();出了什么问题?foo()的返回值不是左值,a也是左值吗?

sometype && b = foo();实际上是右值引用吗?它是否"窃取"foo()的返回值,并将b中的内容发送到析构函数?

有没有其他方法可以不发出警告?

您正在引用一个临时对象。唯一合法的方法是:

const object&(常量l值参考)或

object&&(可变r值参考)

这是一种(故意的)语言限制。

进一步讨论:

将临时对象分配给引用会延长临时对象的生存期,使其与引用的生存期相匹配。因此,令许多初学者惊讶的是,这是合法的:

{
  const string& s = foo();
  cout << s << endl;         // the temporary to which s refers is still alive
}
// but now it's destroyed

然而,将可变引用作为临时引用通常是一个逻辑错误,因此这在以下语言中是不允许的:

{
  string& s = foo();  // this is not possible
  s += "bar";         // therefore neither is this
  // the implication is that since you modified s, you probably want to
  // preserve it
}
// ... but now it's destroyed and you did nothing with it.

这里有一个更现实的原因,为什么它可能是一个逻辑错误,给定:

string foo();         // function returning a string
void bar(string& s);  // this function is asserting that it intends to *modify*
                      // the string you sent it
// therefore:
bar(foo());           // makes no sense. bar is modifying a string that will be discarded.
                      // therefore assumed to be a logic error

您必须将以上内容替换为:

  string s = foo();
  s += "bar";
  // do something here with s

请注意,在命名变量(l-value)中捕获临时变量没有任何开销。

r值引用被设计为移动构造函数或移动赋值的主题。因此,它们是可变的是有道理的。它们的本质意味着物体是瞬态的。

因此,这是合法的:

string&& s = foo();    // extends lifetime as before
s += "bar";
baz(std::move(s));     // move the temporary into the baz function.

这可能有助于您记住,指定&&就是断言您知道变量是可变的临时变量。

但它被允许的真正原因是为了让它发挥作用:

string foo();   // function that returns a string
void bar(string&& s);  // function that takes ownership of s
bar(foo());  // get a string from foo and move it into bar
// or more verbosely:
string s = foo();
bar(move(s));

在c++11之前,bar必须以以下方式之一编写:

void bar(string s);   // copy a string
// resulting in:
const string& s = foo();
bar(s);  // extra redundant copy made here
void bar(const string& s); // const l-value reference - we *may* copy it
// resulting in:
const string& s = foo();
bar(s);  // maybe an extra redundant copy made here, it's up to bar().

sometype&a=foo()?

foo()返回临时,因此您无法将其绑定到引用,因为它在完整表达式(赋值行)结束后将不再存在。延长其使用寿命的唯一方法是将其更改为const sometype & a = foo();或将其分配给右值引用。

是sometype&amp;b=foo();真的重新估价参考?

是的(阅读这里了解更多:右值引用允许悬挂引用吗?)

它是否"窃取"了foo()的返回值,并将b中的内容发送到析构函数?

不,它延长了的使用寿命

有没有其他方法可以不发出警告?

您有三种选择:(1)分配给右值引用,(2)分配给常量左值引用,以及(3)按值返回,但在类中实现移动语义。

您也可以指望编译器会对返回的值执行RVO。