std :: hash专门用于我自己的类,并在类中使用它

时间:2018-11-16 08:27:56

标签: c++ c++11

我定义了一个类Foo,并且希望有一个以std::unordered_set<Foo>作为参数类型的公共成员函数。

要使用std::unordered_set<Foo>,我必须专门研究std::hash<Foo>在命名空间std中的作用。

如果我不尝试在std::unordered_set<Foo>成员函数中使用Foo作为参数类型,那就没关系。

但是,一旦我想在std::unordered_set<Foo>成员函数中使用Foo作为参数类型,就很难定义专业化std::hash<Foo>。 如果在Foo声明之后执行此操作,则Foo声明会出错,因为未定义std::hash<Foo>。它以前是移动std::hash<Foo>的定义,也没有用,因为现在Foo是未知的。 Foo的前向声明在这种情况下不起作用。

有什么办法解决这个问题吗?

这是此类的一个例子

class Foo
{
public:
  std::unordered_set<Foo>::iterator findClosest(std::unordered_set<Foo> const &others)
  {
    return std::end(others);
  }

  size_t hashValue() const {
    return std::hash<int>()(m_Member);
  }

private:
  int m_Member;
};

namespace std
{
  template <>
  struct hash<Foo>
  {
    size_t operator()(Foo const & bar) const
    {
      return bar.hashValue();
    }
  };
}

使用下面的答案,这是我最终使用的代码(由于存在dll,我需要放置一些MY_EXPORT_MACRO):

在文件Foo.h

class Foo;

namespace std
{
  template <>
  struct MY_EXPORT_MACRO hash<Foo>
  {
    size_t operator()(Foo const &bar) const;
  };
}

class MY_EXPORT_MACRO Foo
{
public:
  Foo const *findClosest(std::unordered_set<Foo> const &others);

  size_t hashValue() const
  {
    return std::hash<int>()(m_Member);
  }

  bool operator==(const platypus::Segment2D &other) const
  {
    return m_Member == other.m_Member;
  }

private:
  int m_Member;
};

在文件Foo.cpp

size_t std::hash<Foo>::operator()(Foo const &bar) const
{
  return bar.hashValue();
}

Foo const *Foo::findClosest(std::unordered_set<Foo> const &others)
{
  Foo const *closest = nullptr;
  std::unordered_set<Foo>::const_iterator closestIt =
    std::min_element(std::begin(others), std::end(others), [this](Foo const &lhs, Foo const &rhs) {
      return std::abs(this->m_Member - lhs.m_Member) < std::abs(this->m_Member - rhs.m_Member);
    });

  if (closestIt != std::end(others))
  {
    closest = &(*closestIt);
  }

  return closest;
}

4 个答案:

答案 0 :(得分:3)

您的示例中的真正问题是您想使用std::unordered_set<Foo>::iterator作为返回类型。这要求unordered_set<Foo>完全实例化,这要求Foo(和std::hash<Foo>)是一个完整的类。但是Foo在其定义的末尾只是一个完整的类。请注意,by-reference函数参数不存在此问题,因为编译器不必完全实例化所引用的类。

@MrTux 最初所建议的那样,您可以使用正向声明修复其他所有问题:

class Foo;

template<>
struct std::hash<Foo>;

如果您返回Foo*,则一切正常:

Demo

是否适合您的设计是另一个问题。


我应该注意,您可以 std::hash<Foo>的定义之前完全定义Foo

class Foo;

namespace std
{
  template <>
  struct hash<Foo>
  {
    size_t operator()(Foo const & bar) const;
  };
}

class Foo { /* ... */ };

size_t std::hash<Foo>::operator()(Foo const & bar) const
{
    return bar.hashValue();
}

这将使您返回例如std::unordered_set<Foo>来自Foo中的方法,但仍无法解决Foo在其自身定义中不完整(因此std::unordered_set<Foo>::iterator不可用)的核心问题。

答案 1 :(得分:1)

我看到您已将问题标记为C ++ 11(因此我们不能依赖于类型推导来返回类型),因此这是一个返回const_iteratorothers为const的解决方案):

#include <unordered_set>

class Foo;

namespace std
{
  template <>
  struct hash<Foo>
  {
    size_t operator()(Foo const & bar) const;
  };
}

class Foo
{
public:
  template <class Hash>
  typename std::unordered_set<Foo, Hash>::const_iterator findClosest(std::unordered_set<Foo, Hash> const &others)
  {
    return std::end(others);
  }

  size_t hashValue() const {
    return std::hash<int>()(m_Member);
  }

private:
  int m_Member;
};

size_t std::hash<Foo>::operator()(Foo const & bar) const
{
    return bar.hashValue();
}

int main()
{
    Foo f;
    std::unordered_set<Foo> fs;
    f.findClosest(fs);
}

答案 2 :(得分:1)

如果您将对Foo进行哈希处理并在自由函数中找到最接近的距离的担忧移开,您可能会发现代码更清晰,更易于维护。

这是一个使用boost::hash协议的示例(我发现它非常有用)

#include <unordered_set>
#include <boost/functional/hash.hpp>

class Foo
{
public:

    // return a tuple of immutable values which should be hashed in order to
    // compute this object's hash value
    auto hashy_stuff() const // -> std::tuple<const int&> // if you are < c++14
    {
        return std::tie(m_Member);
    }

private:
  int m_Member;
};

// implement the boost::hash hash_value protocol in terms of hashy_stuff
// (we win because now it's DRY and works with all boost hash functions)
auto hash_value(Foo const& foo) -> std::size_t { return boost::hash_value(foo.hashy_stuff()); }

// implement std::hash in terms of boost::hash (again, DRY because it automatically
// uses hash_value())
namespace std { template<> struct hash<Foo> : ::boost::hash<Foo> {}; }

// implement findClosest as a free function. Its concerns are tangential to
// the Foo-ness of a Foo.
std::unordered_set<Foo>::const_iterator 
findClosest(Foo const& /*foo*/, std::unordered_set<Foo> const &others)
{
    // implementation here in terms of public interface
    return std::end(others);
}

extern auto make_foos() -> std::unordered_set<Foo>;
extern auto make_foo() -> Foo;

bool test()
{
    auto foos = make_foos();
    auto foo = make_foo();

    auto i = findClosest(foo, foos);

    return i != end(foos);
}

答案 3 :(得分:-1)

如果您愿意,可以尝试以下方法来代替在val msg = "This is going to be the message" val url = "https://api.whatsapp.com/send?phone=62"+tempDatas!![position].custHpWa + "&text="+msg val intent = Intent(Intent.ACTION_VIEW) intent.data = Uri.parse(url) startActivity(intent) 命名空间中专门化模板。

intent.setType("text/plain")

Live Code