返回C++迭代器引用

4

正常工作的示例

#include <iostream>
#include <vector>

struct MyClass{


  const std::vector<float>::iterator& begin(){
    return myvec.begin();
  }

  const std::vector<float>::iterator& end(){
    return myvec.end();
  }
  std::vector<float> myvec;
};

int main(){

  std::vector<float> mainvec(8,0);

  MyClass myClass;

  myClass.myvec = mainvec;

  for (std::vector<float>::iterator it = myClass.begin();
      it != myClass.end();++it){
    std::cout << *it << " " ;
  }

  std::cout << std::endl;
}

在这段代码中,我得到了以下输出:
0 0 0 0 0 0 0 0 

一个未能按预期工作的示例:

#include <iostream>
#include <vector>

struct MyClass{


  const std::vector<float>::iterator& begin(){
    return myvec.begin();
  }

  const std::vector<float>::iterator& end(){
    return myvec.end();
  }
  std::vector<float> myvec;
};

int main(){

  std::vector<float> mainvec(8,0);

  MyClass myClass;

  myClass.myvec = mainvec;

  const std::vector<float>::iterator& end_reference = myClass.end();

  for (std::vector<float>::iterator it = myClass.begin();
      it != end_reference;++it){
    std::cout << *it << " " ;
  }

  std::cout << std::endl;
}

在这段代码中,我得到了以下输出:
"empty output"

第一个代码示例

它存在问题,我错误地调用了向量begin()end()而不是MyClass方法。

以下是我提出疑问的最小代码:

#include <iostream>
#include <vector>

struct MyClass{


  const std::vector<float>::iterator& begin(){
    return myvec.begin();
  }

  const std::vector<float>::iterator& end(){
    return myvec.end();
  }
  std::vector<float> myvec;
};

int main(){

  std::vector<float> mainvec(8,0);

  MyClass myClass;

  myClass.myvec = mainvec;

  for (std::vector<float>::iterator it = myClass.myvec.begin();
      it != myClass.myvec.end();++it){
    std::cout << *it << " " ;
  }

  std::cout << std::endl;
}

我在第8行和第12行收到以下警告:

returning reference to local temporary object [-Wreturn-stack-address] [cpp/gcc] 

但当我编译并运行程序时,我得到了以下结果:
0 0 0 0 0 0 0 0 

所以,似乎当返回myvec.begin()时,本地引用并没有被销毁。当我第一次编写代码时,我认为这不会成为一个问题,因为在我的头脑中,向量的begin()方法会返回对第一个向量位置的迭代器引用,而对于我来说,这个迭代器在执行myvec.begin()时并没有被分配,但是它是对此迭代器的引用。所以,这个警告不应该出现,因为我没有分配内存。但由于我不知道这个机制是如何工作的,所以我想学习它以编写一致的代码。看起来我可以忽略这个警告,对吗?

2
你从哪里得到 begin() 返回引用的想法? - user3920237
2
std::vector::begin()的描述中,你看到任何提及引用结果的地方了吗?它返回一个对象 - WhozCraig
2
@Werner:你想知道为什么未定义的行为不会产生明确定义的行为吗?因为它是未定义的。(在这种情况下,它可能只是意味着被销毁对象的存储在复制到本地变量之前还没有被重用,因此它仍然包含旧值。但是你不能依赖这种行为。) - Mike Seymour
1
@Werner:确实,在对象生命期结束后使用对象是未定义行为,而且返回自动变量或临时变量的引用始终是错误的。没有(可移植的)方法可以停止程序在其生命周期结束后重用其内存。您需要通过值返回迭代器。 - Mike Seymour
2
你的 实际 调用代码是有效的,因为你从未调用过 MyClassbegin()end() 成员函数。相反,你直接调用了 myClass.myvec.begin()myClass.myvec.end()。你的 Ub-繁荣成员函数没有被调用。 - WhozCraig
显示剩余5条评论
1个回答

2
“std::vector::begin” 函数确实返回一个临时对象(详见 http://www.cplusplus.com/reference/vector/vector/begin/)。 您的代码是有效的,因为您将这个临时对象绑定到了一个 const 引用上(如果是非 const 引用则不合法),并立即将其复制到了一个局部变量 it 中。
编辑:我的错误。事实上,从一个临时变量返回一个 const 引用也是未定义行为。
然而,将此引用分配给另一个具有更长生命周期的 const 引用将导致未定义行为,因为临时对象的生命周期仅扩展到它直接分配的 const 引用的生命周期。
我建议您考虑警告并返回一个迭代器。
我还建议阅读 http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/

我会阅读这个建议!它似乎正是我需要的补充。然而,我仍然感觉在这个例子中 const 引用(即使是临时对象的有限时间跨度)正是所需的… - Werner
1
我不相信这是正确的。const引用必须至少在与实例化的临时变量相同的作用域内(在Herb指定为有效的所有链接文章示例中都是如此)。但这里并非如此。一旦调用函数返回,临时变量肯定已经消失了。标准的§12.2/5 [class.temporary]描述了这一点,尽管不是最容易理解的术语。如果这不是准确的评估,则我所学习的关于通过const引用进行临时寿命延长的一切都将被抛弃。 - WhozCraig
随着第三段的添加,这看起来是正确的。因此加1。 - WhozCraig

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接