সি ++ 0 এক্স যোগ করে hash<...>(...)
।
উত্সাহhash_combine
হিসাবে উপস্থাপিত হিসাবে আমি যদিও একটি ফাংশন সন্ধান করতে পারেনি । এরকম কিছু বাস্তবায়নের সবচেয়ে পরিষ্কার উপায় কী? সম্ভবত, সি ++ 0 এক্স ব্যবহার করছেন ?xor_combine
উত্তর:
ঠিক আছে, বুস্ট ছেলেরা যেমন করেছে তেমনই এটি করুন:
template <class T>
inline void hash_combine(std::size_t& seed, const T& v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
std::pair
(বা tuple
, এমনকি) জন্য একটি হ্যাশ গ্রন্থাগারের প্রয়োজন । এটি প্রতিটি উপাদানের হ্যাশ গণনা করবে, তারপরে তাদের একত্রিত করবে। (এবং স্ট্যান্ডার্ড লাইব্রেরির চেতনায়, একটি বাস্তবায়নের সংজ্ঞায়িত উপায়ে))
আমি এখানে এটিকে ভাগ করব যেহেতু এটি সমাধানের সন্ধানে অন্যদের পক্ষে এটি কার্যকর হতে পারে: @ কার্লভনমুআর উত্তর থেকে শুরু করে , এখানে একটি বৈকল্পিক টেম্পলেট সংস্করণ রয়েছে, যা আপনাকে বেশ কয়েকটি মানকে একত্রিত করতে হলে এর ব্যবহারের ক্ষেত্রে ক্ষুদ্রতর :
inline void hash_combine(std::size_t& seed) { }
template <typename T, typename... Rest>
inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
hash_combine(seed, rest...);
}
ব্যবহার:
std::size_t h=0;
hash_combine(h, obj1, obj2, obj3);
এটি কাস্টম প্রকারগুলি সহজেই হ্যাশেবল করে তোলার জন্য (যা আমার মনে হয় যে কোনও hash_combine
ফাংশনের প্রাথমিক ব্যবহারগুলির মধ্যে একটি ) এটি একটি ভ্যারিয়েডিক ম্যাক্রো বাস্তবায়নের জন্য মূলত লেখা হয়েছিল :
#define MAKE_HASHABLE(type, ...) \
namespace std {\
template<> struct hash<type> {\
std::size_t operator()(const type &t) const {\
std::size_t ret = 0;\
hash_combine(ret, __VA_ARGS__);\
return ret;\
}\
};\
}
ব্যবহার:
struct SomeHashKey {
std::string key1;
std::string key2;
bool key3;
};
MAKE_HASHABLE(SomeHashKey, t.key1, t.key2, t.key3)
// now you can use SomeHashKey as key of an std::unordered_map
এটি নিম্নলিখিত হিসাবে বৈকল্পিক টেম্পলেট ব্যবহার করেও সমাধান করা যেতে পারে:
#include <functional>
template <typename...> struct hash;
template<typename T>
struct hash<T>
: public std::hash<T>
{
using std::hash<T>::hash;
};
template <typename T, typename... Rest>
struct hash<T, Rest...>
{
inline std::size_t operator()(const T& v, const Rest&... rest) {
std::size_t seed = hash<Rest...>{}(rest...);
seed ^= hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
};
ব্যবহার:
#include <string>
int main(int,char**)
{
hash<int, float, double, std::string> hasher;
std::size_t h = hasher(1, 0.2f, 2.0, "Hello World!");
}
একটি অবশ্যই একটি টেম্পলেট ফাংশন করতে পারে, তবে এটি কিছু বাজে টাইপের ছাড়ের কারণ হতে পারে যেমন hash("Hallo World!")
স্ট্রিংয়ের পরিবর্তে পয়েন্টারে একটি হ্যাশ মান গণনা করবে। এটি সম্ভবত কারণ, কেন স্ট্যান্ডার্ড একটি কাঠামো ব্যবহার করে।
কিছু দিন আগে আমি এই উত্তরের কিছুটা উন্নত সংস্করণ নিয়ে এসেছি (সি ++ 17 সমর্থন প্রয়োজন):
template <typename T, typename... Rest>
void hashCombine(uint& seed, const T& v, Rest... rest)
{
seed ^= ::qHash(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
(hashCombine(seed, rest), ...);
}
উপরের কোডটি কোড তৈরির ক্ষেত্রে আরও ভাল better আমি আমার কোডে Qt থেকে qHash ফাংশনটি ব্যবহার করেছি, তবে অন্য কোনও হ্যাশার ব্যবহার করাও সম্ভব।
(int[]){0, (hashCombine(seed, rest), 0)...};
এবং এটি সি ++ 11 এও কাজ করবে।
আমি vt4a2h দ্বারা উত্তরটি থেকে C ++ 17 পদ্ধতির সত্যই পছন্দ করি , তবে এটি কোনও সমস্যায় ভুগছে: Rest
মানটি দিয়ে চলেছে তবে কনস্টেরেন্স রেফারেন্সগুলি দিয়ে তাদের পাস করা আরও পছন্দনীয় হবে (এটি যদি অবশ্যই হয় তবে এটি আবশ্যক কেবলমাত্র সরানো-প্রকারের সাথে ব্যবহারযোগ্য)।
এখানে অভিযোজিত সংস্করণটি এখনও একটি ভাঁজ এক্সপ্রেশন ব্যবহার করে (এটি কেন এটির জন্য C ++ 17 বা তার বেশি প্রয়োজন) এবং ব্যবহার করে std::hash
(Qt হ্যাশ ফাংশনের পরিবর্তে):
template <typename T, typename... Rest>
void hash_combine(std::size_t& seed, const T& v, const Rest&... rest)
{
seed ^= std::hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
(hash_combine(seed, rest), ...);
}
সম্পূর্ণতার জন্য: এই সংস্করণটির সাথে যে সমস্ত প্রকারের ব্যবহারযোগ্য হবে সেগুলি hash_combine
অবশ্যই নেমস্পেসে ইনজেকশনের জন্য একটি টেম্পলেট বিশেষীকরণ থাকতে হবে ।hash
std
উদাহরণ:
namespace std // Inject hash for B into std::
{
template<> struct hash<B>
{
std::size_t operator()(B const& b) const noexcept
{
std::size_t h = 0;
cgb::hash_combine(h, b.firstMember, b.secondMember, b.andSoOn);
return h;
}
};
}
সুতরাং B
উপরের উদাহরণটিতে টাইপটি অন্য ধরণের ক্ষেত্রেও ব্যবহারযোগ্য A
, যেমন নীচের ব্যবহারের উদাহরণ উদাহরণগুলি দেখায়:
struct A
{
std::string mString;
int mInt;
B mB;
B* mPointer;
}
namespace std // Inject hash for A into std::
{
template<> struct hash<A>
{
std::size_t operator()(A const& a) const noexcept
{
std::size_t h = 0;
cgb::hash_combine(h,
a.mString,
a.mInt,
a.mB, // calls the template specialization from above for B
a.mPointer // does not call the template specialization but one for pointers from the standard template library
);
return h;
}
};
}
Hash
আপনার নিজের কাস্টম হ্যাশারটিকে std
নামস্থানে ইনজেকশন না দিয়ে নির্দিষ্ট করার জন্য স্ট্যান্ডার্ড পাত্রে টেমপ্লেট আর্গুমেন্টগুলি ব্যবহার করা ভাল ।
Vt4a2h এর উত্তর অবশ্যই দুর্দান্ত তবে C ++ 17 ভাঁজ এক্সপ্রেশনটি ব্যবহার করে এবং প্রত্যেকেই সহজেই নতুন সরঞ্জামচঞ্চে স্যুইচ করতে সক্ষম হয় না। নীচের সংস্করণটি ভাঁজ এক্সপ্রেশন অনুকরণ করতে এক্সপেন্ডার ট্রিক ব্যবহার করে এবং সি ++ 11 এবং সি ++ 14 এও কাজ করে।
অতিরিক্তভাবে, আমি ফাংশনটি চিহ্নিত করেছি inline
এবং বৈকল্পিক টেম্পলেট আর্গুমেন্টগুলির জন্য নিখুঁত ফরোয়ার্ডিং ব্যবহার করি।
template <typename T, typename... Rest>
inline void hashCombine(std::size_t &seed, T const &v, Rest &&... rest) {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
(int[]){0, (hashCombine(seed, std::forward<Rest>(rest)), 0)...};
}