散列顶点的最佳方法

时间:2017-10-17 19:26:04

标签: c++ c++11 hash stl

我有以下代码,我想在unordered_set中输入,以便根据其3D坐标进行最快的访问。

struct MeshVertex {
  float x;
  float y;
  float z;

  std::vector<MeshVertex*> links;

  float& operator [](int i) {
    switch (i) {
    case 0:
      return x;
    case 1:
      return y;
    case 2:
      return z;
    default:
      throw std::out_of_range("");
    }
  }

  MeshVertex& operator=(const STLFileVertex& other)
  {
    x = other.x;
    y = other.y;
    z = other.z;
    return *this;
  }

  bool operator<(const MeshVertex& other) const
  {
    if (x == other.x) {
      if (y == other.y)
        return z < other.z;
      return y < other.y;
    }
    return x < other.x;
  }

  bool operator==(const MeshVertex& other) const {
    if (x == other.x && y == other.y && z == other.z)
      return true;
    return false;
  }

  bool operator!=(const MeshVertex& other) const {
    if (x == other.x && y == other.y && z == other.z)
      return false;
    return true;
  }

  double distance(const MeshVertex& other) const {
    return hypot(hypot(x - other.x, y - other.y), z - other.z);
  }
};

正如所料,我收到以下错误:

The C++ Standard doesn't provide a hash for this type.

如何为此实现效果良好的哈希?它只需要包含成员x,y和z以及哈希和比较的组合必须 100%没有冲突,这意味着只有顶点才能被替换新插入的具有完全相同的值。请考虑顶点由float类型表示。这意味着正常的比较可能会产生误导。

编辑:

我真正在做的是阅读STL(立体光刻)二进制文件。那些熟悉STL格式的人会知道三角形在文件中是这样写的:

float normals[3];
float vertex[3][3];
uint16_t attributes;

这意味着在读取文件时,顶点的复制次数会更多。我想规范化顶点(删除重复项)并应用链接来重新创建三角形。

我的目标是通过广度优先搜索映射图表中的所有顶点。如前所述,链接在三角形中可用。获得所有链接后,三角形变为一次性。

如果我没有完美意味着删除重复的顶点,则链接将被破坏,我的映射将失败。

1 个答案:

答案 0 :(得分:1)

有5个简单的步骤可以结束所有std::hash个问题。

TL; DR - 尽早转储std::hash并使用boost。就像c ++ 11以来对我们施加的大部分标准库一样,它在升级库之前是一个前所未有的(并且优于它)的暗影。

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

struct MeshVertex {
  float x;
  float y;
  float z;

  // step one - make your life easier and provide an as_tuple
  // method
  auto as_tuple() const {
      return std::tie(x, y, z);
  }
};

// step 2 all operators in terms of tuple
bool operator == (MeshVertex const& l, MeshVertex const& r)
{
    return l.as_tuple() == r.as_tuple();
}

// step 2 all operators in terms of tuple
bool operator < (MeshVertex const& l, MeshVertex const& r)
{
    return l.as_tuple() < r.as_tuple();
}

// step 3 - use the boost::hash protocol of providing a free function
// called hash_value in the namespace of MeshVertex. std::hash sucks.

std::size_t hash_value(MeshVertex const& arg)
{
    std::size_t seed = 0;
    boost::hash_combine(seed, arg.as_tuple());
    return seed;
}

// step 4 - define your own hash algo to escape from the
// std::hash fiasco.

struct universal_hash
{
    template<class T> std::size_t operator()(T&& arg) const
    {
        // note - boost::hash. A thousand times better.

        using hasher_type = boost::hash<std::decay_t<T>>;
        auto hasher = hasher_type();
        return hasher(arg);
    }
};


int main()
{
    // step 5 - never under any circumstances allow your containers
    // to inflict std::hash on you 

    std::unordered_map<int, MeshVertex, universal_hash> mymap;

    mymap[1] = { 1, 3, 4 };
}