我一直在研究有关使用自定义键对std::tr1::unordered_map
进行增强序列化的一些奇怪行为。在序列化密钥和序列化包含密钥的unordered_map
之间,四种成员存在4种不同的情况:反序列化密钥,反序列化unordered_map
,原始密钥,原始unordered_map
将原始密钥与原始unordered_map
一起使用
将反序列化的密钥与反序列化的unordered_map
一起使用
对原始unordered_map
使用反序列化密钥
对反序列化的unordered_map
使用原始密钥
前两种情况可以按预期工作,但后两种情况无法正确映射。我在下面创建了一个最小的工作示例。请注意,您需要boost标头才能使unordered_map
可序列化。我将其附加在底部。
#include <cstdlib>
#include <unordered_map>
#include <string>
#include <fstream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include "unordered_map.hpp"
class HashKey {
public:
HashKey() = default;
HashKey(const HashKey& orig) = default;
virtual ~HashKey() = default;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & const_cast<unsigned long &>(id);
}
inline bool operator==(const HashKey& key) const {
return this->id == key.id;
}
struct KeyHasher {
std::size_t operator()(const HashKey* key) const {
return boost::hash<unsigned long>()(key->id);
}
};
private:
static unsigned long int idCounter;
const unsigned long int id = HashKey::idCounter;
};
unsigned long int HashKey::idCounter = 0;
int main(int argc, char** argv) {
std::tr1::unordered_map<HashKey*,std::string,HashKey::KeyHasher> map;
HashKey key;
map[&key]="works!";
{
std::ofstream ofs("key.save");
boost::archive::text_oarchive oa(ofs);
oa << key;
oa << map;
}
HashKey newKey;
std::tr1::unordered_map<HashKey*,std::string,HashKey::KeyHasher> newMap;
{
std::ifstream ifs("key.save");
boost::archive::text_iarchive ia(ifs);
ia >> newKey;
ia >> newMap;
}
std::cout<<"Result: "<<map[&key]<<"\n";
std::cout<<"Result: "<<newMap[&newKey]<<"\n";
std::cout<<"Result: "<<map[&newKey]<<"\n";
std::cout<<"Result: "<<newMap[&key]<<"\n";
return 0;
}
运行时此代码的输出为:
Result: works!
Result: works!
Result:
Result:
我不明白为什么后两种情况不起作用。我检查了哈希函数输出的值,它们是正确的。我怀疑这与用作键的指针的
operator()==
有关,但我不确定如何检查。我希望能够在我的代码中使用所有4种情况。关于为什么这不起作用的任何启示?谢谢。这是用于序列化哈希图的
unordered_map.hpp
。这来自this boost ticket。将其包括在MWE中:#ifndef BOOST_SERIALIZATION_UNORDERED_MAP_HPP
#define BOOST_SERIALIZATION_UNORDERED_MAP_HPP
// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// serialization/unordered_map.hpp:
// serialization for stl unordered_map templates
// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org for updates, documentation, and revision history.
#include <boost/tr1/unordered_map.hpp>
#include <boost/config.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/unordered_collections_save_imp.hpp>
#include <boost/serialization/unordered_collections_load_imp.hpp>
#include <boost/serialization/split_free.hpp>
namespace boost {
namespace serialization {
namespace stl {
// map input
template<class Archive, class Container>
struct archive_input_unordered_map
{
inline void operator()(
Archive &ar,
Container &s,
const unsigned int v
){
typedef BOOST_DEDUCED_TYPENAME Container::value_type type;
detail::stack_construct<Archive, type> t(ar, v);
// borland fails silently w/o full namespace
ar >> boost::serialization::make_nvp("item", t.reference());
std::pair<BOOST_DEDUCED_TYPENAME Container::const_iterator, bool> result =
s.insert(t.reference());
// note: the following presumes that the map::value_type was NOT tracked
// in the archive. This is the usual case, but here there is no way
// to determine that.
if(result.second){
ar.reset_object_address(
& (result.first->second),
& t.reference().second
);
}
}
};
// multimap input
template<class Archive, class Container>
struct archive_input_unordered_multimap
{
inline void operator()(
Archive &ar,
Container &s,
const unsigned int v
){
typedef BOOST_DEDUCED_TYPENAME Container::value_type type;
detail::stack_construct<Archive, type> t(ar, v);
// borland fails silently w/o full namespace
ar >> boost::serialization::make_nvp("item", t.reference());
BOOST_DEDUCED_TYPENAME Container::const_iterator result
= s.insert(t.reference());
// note: the following presumes that the map::value_type was NOT tracked
// in the archive. This is the usual case, but here there is no way
// to determine that.
ar.reset_object_address(
& result->second,
& t.reference()
);
}
};
} // stl
template<
class Archive,
class Key,
class HashFcn,
class EqualKey,
class Allocator
>
inline void save(
Archive & ar,
const std::tr1::unordered_map<
Key, HashFcn, EqualKey, Allocator
> &t,
const unsigned int /*file_version*/
){
boost::serialization::stl::save_unordered_collection<
Archive,
std::tr1::unordered_map<
Key, HashFcn, EqualKey, Allocator
>
>(ar, t);
}
template<
class Archive,
class Key,
class HashFcn,
class EqualKey,
class Allocator
>
inline void load(
Archive & ar,
std::tr1::unordered_map<
Key, HashFcn, EqualKey, Allocator
> &t,
const unsigned int /*file_version*/
){
boost::serialization::stl::load_unordered_collection<
Archive,
std::tr1::unordered_map<
Key, HashFcn, EqualKey, Allocator
>,
boost::serialization::stl::archive_input_unordered_map<
Archive,
std::tr1::unordered_map<
Key, HashFcn, EqualKey, Allocator
>
>
>(ar, t);
}
// split non-intrusive serialization function member into separate
// non intrusive save/load member functions
template<
class Archive,
class Key,
class HashFcn,
class EqualKey,
class Allocator
>
inline void serialize(
Archive & ar,
std::tr1::unordered_map<
Key, HashFcn, EqualKey, Allocator
> &t,
const unsigned int file_version
){
boost::serialization::split_free(ar, t, file_version);
}
// unordered_multimap
template<
class Archive,
class Key,
class HashFcn,
class EqualKey,
class Allocator
>
inline void save(
Archive & ar,
const std::tr1::unordered_multimap<
Key, HashFcn, EqualKey, Allocator
> &t,
const unsigned int /*file_version*/
){
boost::serialization::stl::save_unordered_collection<
Archive,
std::tr1::unordered_multimap<
Key, HashFcn, EqualKey, Allocator
>
>(ar, t);
}
template<
class Archive,
class Key,
class HashFcn,
class EqualKey,
class Allocator
>
inline void load(
Archive & ar,
std::tr1::unordered_multimap<
Key, HashFcn, EqualKey, Allocator
> &t,
const unsigned int /*file_version*/
){
boost::serialization::stl::load_unordered_collection<
Archive,
std::tr1::unordered_multimap<
Key, HashFcn, EqualKey, Allocator
>,
boost::serialization::stl::archive_input_unordered_multimap<
Archive,
std::tr1::unordered_multimap<
Key, HashFcn, EqualKey, Allocator
>
>
>(ar, t);
}
// split non-intrusive serialization function member into separate
// non intrusive save/load member functions
template<
class Archive,
class Key,
class HashFcn,
class EqualKey,
class Allocator
>
inline void serialize(
Archive & ar,
std::tr1::unordered_multimap<
Key, HashFcn, EqualKey, Allocator
> &t,
const unsigned int file_version
){
boost::serialization::split_free(ar, t, file_version);
}
} // namespace serialization
} // namespace boost
#endif // BOOST_SERIALIZATION_UNORDERED_MAP_HPP
最佳答案
因此,老实说,我不完全了解发生了什么,因为我不知道如何:
newMap.contains(&newKey)
可以是真的。由于您要存储指向键的指针,因此
newKey
的地址反序列化时不应更改,因此它不可能在新地图中。就是说,您可能想使一切变得简单的只是...不使用指针:typedef std::tr1::unordered_map<HashKey,
std::string,
HashKey::KeyHasher> MapType;
HashKey key;
MapType map;
map[key] = "works";
// serialize stuff...
HashKey newKey;
MapType newMap;
// deserialize stuff...
assert(map.contains(key));
assert(map.contains(newKey));
assert(newMap.contains(key));
assert(newMap.contains(newKey));
这将涉及修复您的
KeyHasher
以采用HashKey&
而不是HashKey*
。如果两个键相等,则它们应该是可互换的。如果使用指针,最终将依赖于指针相等,而后者完全与值相等无关(p1 == p2
表示*p1 == *p2
,但并非相反)。如果您确实坚持将KeyType设置为
HashKey*
,则可以执行以下操作:struct Pred {
bool operator()(const HashKey* a, const HashKey* b) const {
return (*a) == (*b);
}
};
typedef std::tr1::unordered_map<HashKey*,
std::string,
HashKey::KeyHasher,
Pred> MapType;
但是我建议第一种方法。
关于c++ - 使用Boost序列化,std::tr1::unordered_map和自定义键的奇怪行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26693646/