【问题标题】:unordered_map::find() and two iteratorsunordered_map::find() 和两个迭代器
【发布时间】:2015-01-10 14:09:27
【问题描述】:

拥有一个带有私有成员的类

std::unordered_map<std::string, size_t> myMap;

以及对应的getter

std::unordered_map<std::string, size_t> getMyMap() const {return myMap;}

我通过两次应用 std::unordered_map::find() 观察到奇怪的行为,每次都保存返回的迭代器,例如

auto pos1 = test.getMyMap().find("a");
auto pos2 = test.getMyMap().find("a");

尽管我寻找相同的键“a”,但迭代器指向不同的元素。下面的示例代码说明了这个问题:

#include <iostream>
#include <unordered_map>
#include <vector>
#include <string>

class MyMap{
 public:
  MyMap(){
    myMap= {
      {"a", 1},
      {"b", 2}
    };
  }

  std::unordered_map<std::string, size_t> getMyMap() const {return myMap;}

private:
  std::unordered_map<std::string, size_t> myMap;
};

int main(){

  MyMap test;

  auto pos1 = test.getMyMap().find("a");
  auto pos2 = test.getMyMap().find("a");
  std::cout << pos1->first << "\t" << pos1->second << std::endl;
  std::cout << pos2->first << "\t" << pos2->second << std::endl;
}

使用 g++ -std=c++11 编译并运行给出

b   2
a   1

第一行出乎意料的地方。它应该是“1”。

把代码改成

  auto pos3 = test.getMyMap().find("a");
  std::cout << pos3->first << "\t" << pos3->second << std::endl;
  auto pos4 = test.getMyMap().find("a");
  std::cout << pos4->first << "\t" << pos4->second << std::endl;

产生正确的输出

a   1
a   1

此外,只需在主文件中创建一个 unordered_map 并应用 find() 即可。似乎问题与 getter 方法有关,可能与返回值优化有关。你对这种现象有什么解释吗?

【问题讨论】:

    标签: class c++11 iterator return-value unordered-map


    【解决方案1】:

    这是因为您的代码中有undefined behaviorgetMyMap 返回地图的副本,该副本在表达式 test.getMyMap().find("a") 完成后被破坏。

    这意味着您有两个指向不再存在的地图的迭代器。

    解决方案很简单:让getMyMap 改为返回一个常量引用:

    std::unordered_map<std::string, size_t> const& getMyMap() const;
    

    它似乎在后一种情况下起作用,因为这是一个未定义行为的陷阱,它有时可能看起来像它起作用,而实际上它不起作用。

    【讨论】:

      【解决方案2】:

      test.getMyMap().find("a"); 在原始myMap 的副本上执行find,该副本在表达式完成后被破坏,使迭代器pos1pos2 变为不存在的映射,调用未定义的行为

      相反,您可以如下:

        auto mymap = test.getMyMap() ;   // Store a copy
      
        auto pos1 = mymap.find("a");    // Then do stuff on copy
        auto pos2 = mymap.find("a");
      

      【讨论】:

      • 我觉得有点愚蠢......@JoachimPileborg 的回答在我的情况下给出了完美的解决方案。非常感谢你们!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多