【问题标题】:Understanding the warning: binding r-value to l-value reference理解警告:将右值绑定到左值引用
【发布时间】:2015-12-12 14:11:33
【问题描述】:

我想通过引用传递一个结构,这样它就不会被复制,但是 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 中的内容发送到析构函数?

有没有其他方法可以不显示此警告?

【问题讨论】:

    标签: c++ c++11 syntax resharper rvalue


    【解决方案1】:

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

    const object&(常量左值引用),或

    object&&(可变右值引用)

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

    进一步讨论:

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

    {
      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
    

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

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

    因此,这是合法的:

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

    这可能会帮助您记住,指定 &amp;&amp; 是在断言您知道该变量是一个可变临时变量。

    但它被允许的真正原因是这样可以工作:

    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().
    

    【讨论】:

    • object&amp;&amp; 是否被视为右值引用?
    • 另外,将 const 添加到 const object&amp; 背后的动机是什么?
    • 是的。在这种情况下,它是一个右值引用。
    • 最初的想法是对临时对象进行可变引用是没有意义的,因为临时对象会被丢弃。在临时对象上调用可变方法通常是一个逻辑错误,因此是不允许的。指定一个 r 值参考信号基本上表明您知道自己在做什么。
    • 我不知道它臭名昭著,但在 c++11 之前它肯定是不可能的:)
    【解决方案2】:

    sometype & a = foo(); 有什么问题? ?

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

    是某种类型 && b = foo();实际上是右值引用?

    是的(阅读此处了解更多信息:Do rvalue references allow dangling references?

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

    不,它延长了它的使用寿命

    是否有其他方法可以不显示此警告?

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

    您还可以指望编译器将对返回值执行 RVO。

    【讨论】:

      猜你喜欢
      • 2017-12-06
      • 2014-10-31
      • 2017-04-13
      • 2014-01-02
      • 1970-01-01
      • 1970-01-01
      • 2018-09-30
      • 1970-01-01
      相关资源
      最近更新 更多