按值将向量返回到引用中

时间:2016-05-18 21:16:56

标签: c++

我有以下代码:

std::vector<Info*> filter(int direction)
{
    std::vector<Info*> new_buffer;
    for(std::vector<Info*>::iterator it=m_Buffer.begin();it<m_Buffer.end();it++)
    {
        if(((*it)->direction == direction)
        {
            new_buffer.push_back(*it);
        }
    }

    return new_buffer;
}

std::vector<Info*> &filteredInfo= filter(m_Direction);

有人可以解释这里发生了什么吗? filter方法是否按值返回会创建一个临时的而且filteredInfo永远不会被销毁,因为它是一个引用?

不确定我是否理解正确。在这种情况下,filteredInfo与引用之间的差异是什么?

3 个答案:

答案 0 :(得分:6)

您的编译器应该抱怨该代码。

本声明:

<div id="member-info">
  <div class="cell-center">Member since June 2015</div>
  <div class="cell-right">&Theta;</div>
</div>

std::vector<Info*> &filteredInfo= filter(m_Direction); 的位置是个坏主意:

filter

您正在尝试创建对临时对象的引用。即使它与您的编译器成功,也是非法的。

您应该使用:

std::vector<Info*> filter(int direction);

它的效率与你想要的一样。要么std::vector<Info*> filteredInfo= filter(m_Direction); 操作(C ++ 11)会在那里发生,要么Return Value Optimization会启动。对于move的实现,它应该是优化版本上的RVO(它取决于但是你的编译器质量

但是,您应该注意到您正在将原始指针复制到向量中,我希望您拥有正确的所有权模型?如果没有,我建议你使用智能指针。

答案 1 :(得分:2)

以下是发生的事情:

  1. std::vector<Info*> new_buffer;在本地创建一个对象。
  2. return new_buffer;在调用new_buffer时将filter(m_Direction)移至临时对象。
  3. 现在,如果您致电std::vector<Info*> filteredInfo= filter(m_Direction);,则临时对象将被移至filteredInfo,因此没有不必要的副本,而且这是最有效的方式。
  4. 但是,如果你打电话给std::vector<Info*> &filteredInfo= filter(m_Direction);,那么filteredInfo被绑定到一个临时对象,这是一个很糟糕的主意,大多数编译器会抱怨这个。

答案 2 :(得分:2)

在这里你正确地感到困惑,因为有两个独立的奇怪事实混合在一起:

  • 您的编译器允许将非const引用绑定到临时引用。这在历史上是微软编译器中的一个错误,并且标准不允许这样做。该代码不应该编译。
  • 然而,奇怪的是,标准实际上允许绑定const对临时对象的引用,并且对此有一个特殊规则:临时对象不会立即被销毁(就像它会正常发生)但它的生命将延长参考的生活。

在代码中:

std::vector<int> foo() {
    std::vector<int> x{1,2,3};
    return x;
}

int main() {
    const std::vector<int>& x = foo(); // legal
    for (auto& item : x) {
        std::cout << x << std::endl;
    }
}

关于将const引用绑定到temporaries这个显然荒谬的规则的原因是在C ++中有一个非常常见的“模式”(1)传递const引用而不是参数值,即使身份是无关紧要的。如果将此(反)模式与隐式转换结合起来,会发生的情况是:例如:

void foo(const std::string& x) { ... }

无法使用

进行调用
foo("Hey, you");

没有特殊规则,因为const char *(文字)被隐式转换为临时std::string并作为参数绑定到const引用。

(1)从哲学的角度来看,模式确实非常糟糕,因为值是值,引用是引用:这两个是逻辑上不同的概念。 A const reference is not a value and confusing the two can be the source of very subtle bugs。然而,C ++是性能痴迷的,特别是在移动语义之前,传递const引用被认为是传递值的“智能”方式,尽管由于生命周期和别名问题而导致问题,并且使优化器更难以实现。使用现代编译器传递引用应仅用于“大”对象,尤其是那些即时构建的对象,或者当您真正对对象标识感兴趣而不仅仅是对象价值。