【问题标题】:Why a default constructor is needed using unordered_map and tuple?为什么需要使用 unordered_map 和元组的默认构造函数?
【发布时间】:2015-04-23 14:16:01
【问题描述】:

在下面的程序中,我将一些信息存储在一个哈希表(std::unordered_map)中,key是RectData类的一个对象,关联的值是一个元组和自定义KeyHash和KeyEqual 已定义。

在没有默认构造函数的情况下插入 对会在 gcc 4.9.2 中出现两页错误。第一个错误的行是:

visited_info[rect0] = info0;

我仔细检查了 MSVC++ 12.0,我也有错误消息。

当我添加默认构造函数时,编译正常并且在运行时调用默认构造函数。我不明白为什么 RectData 类需要默认构造函数?

从哈希表中取数据,使用[]操作符,编译时也需要默认构造函数,但运行时不调用,为什么?

auto info = visited_info[rect];

注意:使用visited_info.emplace() 和visited_info.find() 更改代码可以解决问题,但不能回答问题。

感谢您的回答。

完整代码如下。

#include <boost/functional/hash.hpp>
#include <tuple>
#include <vector>
#include <unordered_map>
#include <iostream>

using uint = unsigned int;

enum class Direction : int { left = 0, right = 1, up = 2, down = 3, null = 4 };

class RectData {
 public:
  RectData(uint width, uint height)
      : width_(width), height_(height), datas_(width * height, 0) {
    total_ = width_ * height_;
  }


  // A default constructor must be defined!
  RectData() : RectData(0u, 0u) {
    std::cout << "Calling the default constructor !!!" << std::endl;
  }

  size_t hash() const {
    return boost::hash_value(datas_);
  }

  bool operator==(const RectData &rect) const {
    return (width_ == rect.width_) &&
           (height_ == rect.height_) &&
           (datas_ == rect.datas_);
  }

  struct KeyHash {
    std::size_t operator()(const RectData &rect) const {
      return rect.hash();
    }
  };

  struct KeyEqual {
    std::size_t operator()(const RectData &r1, const RectData &r2) const {
      return r1 == r2;
    }
  };

 private:
  uint width_;
  uint height_;
  std::vector<uint> datas_;
  uint total_;
};

using StoredInfo = std::tuple<uint, RectData, Direction>;

int main() {
  std::unordered_map<RectData, StoredInfo, RectData::KeyHash,
                     RectData::KeyEqual> visited_info;

  RectData rect0(5u, 5u);
  RectData rect1(4u, 4u);
  RectData rect2(3u, 3u);
  RectData rect3(2u, 2u);

  StoredInfo info0 = std::make_tuple(10u, rect1, Direction::up);
  StoredInfo info1 = std::make_tuple(11u, rect2, Direction::down);
  StoredInfo info2 = std::make_tuple(12u, rect3, Direction::left);
  StoredInfo info3 = std::make_tuple(13u, rect0, Direction::right);


  // the line below requires a default RectData constructor!!! 
  visited_info[rect0] = info0;

  // default RectData constructor also needed here !!!
  visited_info[rect1] = std::move(info2);

  // but not needed here
  visited_info.insert(std::make_pair(rect2, info2));

  // and not needed here
  visited_info.emplace(rect3, info3);

  // but needed here and not called!!! 
  StoredInfo i1 = visited_info[rect1];
  std::cout << "Verify (must be 11) = " << std::get<0>(i1)
            << std::endl;

  // but needed here and not called!!! 
  StoredInfo &i2 = visited_info[rect2];
  std::cout << "Verify (must be 12) = " << std::get<0>(i2)
            << std::endl;


  // and not needed here
  auto it = visited_info.find(rect3); 
  std::cout << "Verify (must be 13) = " << std::get<0>(it->second)
            << std::endl;

}

【问题讨论】:

  • visited_info[rect0] = info0; 中,首先调用operator[],它别无选择,只能默认构造一个新元素并返回对它的引用。然后在该新元素上调用 operator=。与 auto info = visited_info[rect]; 相同 - operator[] 需要默认构造函数,以防具有给定键的条目不存在。如果该条目确实存在,则返回对现有元素的引用并且不调用默认构造函数。
  • 明白了,感谢您对 operator[] 的默认构造的清晰解释。

标签: c++ c++11 stl


【解决方案1】:
visited_info[rect0] = info0;

嗯,你觉得这有什么作用? well-documented 左侧评估为对存储在地图中的项目的引用。如果该项目之前不存在,则默认先构建它。

然后,您可以使用复制或移动分配从表达式的右侧更新默认构造的项目。

如果您想避免现在得到的默认构造和分配操作,请改用emplace


注意。一个可能的混淆来源是,例如 Python,其中MyObj[1] 可能转换为__getitem__ 调用,但MyObj[1]=1 转换为__setitem__ 调用。

在 C++ 中,左边和右边的表达式都必须计算为 something,而不知道它们所在的语句。因此,左边的 - side 评估为您可以读取或分配给的引用 - 但对象需要存在才能获取该引用。

【讨论】:

  • 哇,很好~但我发现 DefualtInsertable 是从 en.cppreference.com/w/cpp/container/unordered_map/operator_at 修复的?你知道意思吗?如果我们不需要默认构造的属性,我们如何在分配之前创建一个变量?很有趣。
  • 这表示您仍然需要将值设为 DefaultConstructible,除非您有一个自定义分配器,当按照描述调用时,它可以使用非默认构造函数构造它(即,就好像在没有值构造函数参数的情况下放置一样)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-01
  • 2012-12-25
  • 1970-01-01
  • 2017-01-10
  • 2021-06-25
  • 2013-03-20
相关资源
最近更新 更多