我定义了一个类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;
}
答案 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*
,则一切正常:
是否适合您的设计是另一个问题。
我应该注意,您可以 在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_iterator
(others
为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")