STL地图可以用于不同大小的密钥吗?
我没有此代码。我仍在试图弄清楚是否可以这样做,因此我的问题。 (我是那种可以在一个不可能解决的问题上花费太多时间的人。我希望从你的智慧中学习。)
我正在研究一个基本上有两个键的查找表。数字类型键和类型特定的辅助键。
例如,第一级键是枚举:
enum key_type {
E_ERROR = 0,
E_INT = 1,
E_CHAR = 2,
E_STR = 3,
}; // Yes I know you don't HAVE to specify the values for the enumeration
然后辅助键取决于key_type。 E_INT
的辅助键是整数,E_CHAR
是字符等。
Key: E_INT
2ndary Key Examples: 1, 2, 3, 4
Key: E_CHAR
2ndary Key Examples: 'a', 'b', 'c', 'd'
Key: E_STR
2ndary Key Examples: "abc", "xyz", "pdq", "jrr"
我的第一反应是使这个地图指针数组。第一级键用作数组的索引。数组索引指向支持辅助键类型的映射。
+--------+
| E_INT |------------------------------>+------------------+
+--------+ | MAP with INT key |
| E_CHAR |---------------\ +------------------+
+--------+ \
| E_STR |------\ \---->+-------------------+
+--------+ \ | MAP with CHAR key |
\ +-------------------+
\
\------>+------------------+
| MAP with STR key |
+------------------+
我知道我可以让上述工作正常工作,但我想我可以将这两个键组合在一起并使用一个自定义sort()
算法来处理组合密钥。
考虑到这一点,我完全疯了吗?如果不是坚果,你对如何处理这个有什么建议吗?
在我的脑海中,我需要为key创建一个继承类,其中基类为sort方法提供纯虚函数,然后继承E_INT
的{{1}},{{1}和} E_CHAR
,实现E_STR
方法的用法。然后我会使用基本类作为地图的关键。
评论
编辑8/13/2010
我一直在尝试提出一些解决方案,以及我原来的想法。我一直在遇到问题。我偶然发现了另一篇提到type erasure的stackoverflow文章,该文章可能为我的不同密钥提供了技巧。
编辑2010年8月16日
在下面的答案部分添加了一个答案,其中显示了我实施的编码解决方案。
答案 0 :(得分:4)
std::map
要求密钥strict weak ordering。如果您可以使用自定义比较器对不同的键类型强制执行单个订单,那么它应该不是问题。
答案 1 :(得分:2)
我认为您的方法在使用自定义密钥方面是正确的。
那就是说,如果你可以节省N个地图与一个地图和自定义键的开销,我会说这样做,因为它很简单而且很快。你甚至可以懒得加载地图,只是将实现隐藏在另一个类后面。
编辑:您的自定义比较器也应该很简单。您可以先通过枚举严格排序,然后对于具有相同枚举值(CHAR,INT,STR等)的键,您应该只按值进行比较。这将保证std :: map所需的排序。
答案 2 :(得分:2)
虽然提出了许多解决方案......但没有一个像以下一样优雅:
typedef boost::variant<int,char,std::string> key_type;
typedef std::map<key_type, value_type> map_type;
是的,就是这样。 boost::variant
首先按类型排序,第二种(当类型相同时)按其携带的值排序(如果可以比较)。
我挑战任何人找到一个更简单的解决方案;)
答案 3 :(得分:1)
您需要将两个键包装成一个对象。您的新课程也需要具有可比性。例如:
struct myKey
{
int key1;
std::string key2;
bool operator<(const myKey&) const;
};
没有什么能阻止key1和key2成为(智能)指针。一旦一个对象(如myKey)通过&lt;运算符,它可以在地图中使用。
答案 4 :(得分:1)
如果将boost :: Any与http://www.sgi.com/tech/stl/Map.html中的比较运算符结合使用。只要您的操作员可以订购它们,您就应该能够使用多种类型作为密钥。如果你使用boost :: Any,它们将占用比密钥本身更多的空间,但是这里显示的其余解决方案也会产生一些开销。
答案 5 :(得分:0)
您是否必须将所有列表排在一起?如果是这样,那么当您尝试访问元素时,您的方法将会很痛苦。
一种愚蠢的方法是简单地在联合上创建联合类型和比较函数:
typedef struct IntRecord {
key_type key;
int record;
};
typedef struct CharRecord {
key_type key;
char record;
};
typedef struct StrRecord {
key_type key;
const char * record;
};
typedef struct Base {
key_type key;
};
typedef union Record {
Base b;
IntRecord i;
CharRecord c;
StrRecord s;
};
现在你的比较函数可以查看每个记录的b.key以查看应该使用的类型。
答案 6 :(得分:0)
您可以为实现<
运算符的键实现一个类,类似这样(未经测试):
struct UnionMapKey {
int key_type;
union {
Error *err; // maybe pointer because complex types can't be in C unions
int i;
char c;
string *s; // pointer because complex types can't be in C unions
};
UnionMapKey(const string &stringContent) { key_type = E_STR; s = new string(stringContent); }
// other constructor overrides
bool operator<(const UnionMapKey &rhs) {
if (key_type != rhs.key_type) return key_type < rhs.key_type;
if (key_type == E_ERROR) return err < rhs.err;
// etc.
}
~UnionMapKey() {
if (key_type == E_STR) delete s;
}
};
你可能还需要一个复制构造函数,但我认为你明白了。一旦你解决皱纹,这将作为地图键正常工作。老实说,如果适用于您的地图阵列解决方案可能会更简单。
答案 7 :(得分:0)
我实施的解决方案
共识是可以做到的。我对type erasure概念以及上面其他人的建议进行了改编。我有一些现在有用的东西。 map键必须是一个具有指向多态键对象的指针的对象。
我尝试只使用基础对象作为键类型,但是当地图创建其键的副本时,看起来它只是复制基类。
所以我天真地切换到指针(key_base_c *
)。然而,这只是做指针比较。我甚至没有使用我的分类。
阅读type erasure信息后。我将我的指针解决方案放在一个multi_key_c
对象内,该对象将其<
,==
和strIdx()
调用转发到我隐藏在key_base_c
内的#include <map>
#include <sstream>
#include <iostream>
#include <utility>
//
// list of types to act as the primary key. The primary key dicatates the
// format of the secondary key.
//
enum e_types {
E_ERROR = 0,
E_INT = 1,
E_CHAR = 2,
E_STR = 3,
};
// Base class for the multi-key.
class key_base_c {
public:
key_base_c (enum e_types key_type) :
key1(key_type)
{};
virtual ~key_base_c(void) {};
virtual std::string strIdx (void) const {
std::stringstream ss_idx;
ss_idx << key1;
return (ss_idx.str());
}
virtual bool operator< (const key_base_c &b) const {
return (key_base_c::operator<(&b));
}
virtual bool operator< (const key_base_c *p) const {
return (key1 < p->key1);
}
virtual bool operator== (const key_base_c &b) const {
return (key_base_c::operator==(&b));
}
virtual bool operator== (const key_base_c *p) const {
return (key1 == p->key1);
}
protected:
e_types key1; // the primary key
};
// template policy_key_c
//
// EVENT_TYPE_VAL - select the enumerated value to use for key1's value
//
// KEY2_TYPE - select the class to use for the second key. For built
// in types they use their default < and == operators,
// If a private custom type is specified then it must
// have its own < and == operators specified
//
template <enum e_types EVENT_TYPE_VAL,
class KEY2_TYPE>
class policy_key_c : public key_base_c
{
public:
policy_key_c (KEY2_TYPE key_value) :
key_base_c(EVENT_TYPE_VAL),
key2(key_value)
{};
virtual ~policy_key_c(void) {};
// return the index as a string.
virtual std::string strIdx (void) const {
std::stringstream ss_idx;
ss_idx << key_base_c::strIdx() << "." << key2;
return (ss_idx.str());
}
//
// operator <
//
virtual bool operator< (const key_base_c &b) const {
return (operator<(&b));
}
virtual bool operator< (const key_base_c *p) const {
// if the primary key is less then it's less, don't check 2ndary
if (key_base_c::operator<(p)) {
return (true);
}
// if not less then it's >=, check if equal, if it's not equal then it
// must be greater
if (!(key_base_c::operator==(p))) {
return (false);
}
// primary keys are equal, so now check the 2ndary key. Since the
// primary keys are equal then that means this is either a key_base_c
// object or its a policy_key_c object.
const policy_key_c *p_other = dynamic_cast<const policy_key_c*>(p);
// if NULL then it was a key_base_c, and has no secondary key, so it is
// lexigraphically smaller than us, ergo we are not smaller than it.
if (!p_other) {
return (false);
}
return (key2 < p_other->key2);
}
//
// operator ==
//
virtual bool operator== (const key_base_c &b) const {
return(operator==(&b));
}
virtual bool operator== (const key_base_c *p) const {
// if the primary key isn't equal, then we're not equal
if (!(key_base_c::operator==(p))) {
return (false);
}
// primary key is equal, so now check the secondary key. Since the
// primary keys are equal, then that means this is eitehr a key_base_c
// object or its a policy_key_c object.
const policy_key_c *p_other = dynamic_cast<const policy_key_c*>(p);
// if NULL then it was a key_base_c
if (!p_other) {
// why? If the LHS is a key_base_c it doesn't go any deeper than
// the base. Hence we don't either.
return (true);
}
return (key2 == p_other->key2);
}
protected:
KEY2_TYPE key2; // The secondary key.
};
class multi_key_c {
public:
multi_key_c (key_base_c *p) :
p_key(p)
{};
~multi_key_c(void) {};
bool operator< (const multi_key_c &mk) const {
return (p_key->operator<(mk.p_key));
}
bool operator== (const multi_key_c &mk) const {
return (p_key->operator==(mk.p_key));
}
std::string strIdx (void) const {
return (p_key->strIdx());
}
protected:
key_base_c *p_key;
};
// DO_TEST(x, op, y)
// x, y: can be any derived key type
// op : The operation to do < or ==
//
// Prints the operation being done along with the results of the operation
// For example:
// DO_TEST(a, <, b)
// will print:
// a < b: <results>
//
// where <results> are the results of the operation 'a < b'
#define DO_TEST(x, op, y, expect) \
{ \
bool retval = x op y; \
std::cout << #x " " #op " " #y ": " << retval \
<< " = " << ((retval == expect) ? "pass" : "----FAIL") << "\n"; \
}
template <class C>
void
print_them (C **pp_c,
int count,
std::string s_type)
{
int idx;
std::cout << "\n" << count << " keys for " << s_type << "\n";
for (idx = 0 ; idx < count; ++idx) {
std::cout << " " << (*pp_c)->strIdx() << "\n";
pp_c++;
}
}
int
main (void)
{
std::cout << "\nBASE\n";
key_base_c base_error(E_ERROR), base_int(E_INT), base_char(E_CHAR);
key_base_c base_str(E_STR);
key_base_c *key_base_array[] = {
&base_error, &base_int, &base_char, &base_str
};
print_them(key_base_array,
(sizeof(key_base_array) / sizeof(key_base_array[0])),
"key_base_c");
DO_TEST(base_error, < , base_error, false);
DO_TEST(base_error, < , base_int, true);
DO_TEST(base_int, < , base_char, true);
DO_TEST(base_char, < , base_str, true);
std::cout << "\n";
DO_TEST(base_error, ==, base_error, true);
DO_TEST(base_int, ==, base_int, true);
DO_TEST(base_char, ==, base_char, true);
DO_TEST(base_str, ==, base_str, true);
std::cout << "\n";
DO_TEST(base_error, ==, base_int, false);
DO_TEST(base_int, ==, base_char, false);
DO_TEST(base_char, ==, base_str, false);
// INT
//
typedef policy_key_c<E_INT, int> key_int_2;
key_int_2 i1(1), i2(2), i3(3), i4(4);
key_int_2 *key_int2_array[] = {
&i1, &i2, &i3, &i4,
};
print_them(key_int2_array,
(sizeof(key_int2_array) / sizeof(key_int2_array[0])),
"key_int_2");
DO_TEST(base_int, < , i1, false);
DO_TEST(i1, < , base_int, false);
DO_TEST(i1, < , base_char, true);
DO_TEST(base_char, < , i1, false);
DO_TEST(i1, ==, i1, true);
DO_TEST(i1, ==, base_int, true);
DO_TEST(base_int, ==, i1, true);
DO_TEST(i1, ==, base_error, false);
DO_TEST(base_error, ==, i1, false);
std::cout << "\n";
DO_TEST(i1, < , i2, true);
DO_TEST(i1, < , i3, true);
DO_TEST(i1, < , i4, true);
// CHAR
typedef policy_key_c<E_CHAR, char> key_char_c;
key_char_c c1('a'), c2('b'), c3('c'), c4('d');
key_char_c *key_char_array[] = {
&c1, &c2, &c3, &c4,
};
print_them(key_char_array,
(sizeof(key_char_array) / sizeof(key_char_array[0])),
"key_char");
DO_TEST(base_int, < , c1, true );
DO_TEST(base_int, ==, c1, false);
DO_TEST(base_char, < , c1, false);
DO_TEST(base_char, ==, c1, true );
DO_TEST(base_str, < , c1, false);
DO_TEST(base_str, ==, c1, false);
std::cout << "\n";
DO_TEST(c1, < , c1, false);
DO_TEST(c1, ==, c1, true );
DO_TEST(c1, < , c2, true );
DO_TEST(c1, ==, c2, false);
std::cout << "\n";
DO_TEST(c1, ==, i1, false);
DO_TEST(i1, ==, c1, false);
DO_TEST(c1, < , i1, false);
DO_TEST(i1, < , c1, true );
// STR
typedef policy_key_c<E_STR, std::string> key_str_c;
key_str_c s1("aaa"), s2("bbb"), s3("ccc"), s4("ddd");
key_str_c *key_str_array[] = {
&s1, &s2, &s3, &s4
};
print_them(key_str_array,
(sizeof(key_str_array) / sizeof(key_str_array[0])),
"key_str");
DO_TEST(base_int, < , s1, true );
DO_TEST(base_char, < , s1, true );
DO_TEST(base_str, < , s1, false);
DO_TEST(base_str, ==, s1, true );
DO_TEST(s1, < , base_int, false);
DO_TEST(s1, < , base_char, false);
DO_TEST(s1, < , base_str, false);
DO_TEST(s1, ==, base_str, true);
std::cout << "\n";
DO_TEST(s1, < , s1, false);
DO_TEST(s1, ==, s1, true );
DO_TEST(s1, < , s2, true );
DO_TEST(s1, ==, s2, false);
std::cout << "\n\nNOW TESTING THE MAP\n\n";
typedef std::multimap<multi_key_c, std::string> multiKeyMap;
multiKeyMap myMap;
multi_key_c k1(&i1), k2(&i2), k3(&i3), k4(&i4);
multi_key_c k5(&c1), k6(&c2), k7(&c3), k8(&c4);
multi_key_c k9(&s1), k10(&s2), k11(&s3), k12(&s4);
myMap.insert(std::make_pair(k1, "one"));
myMap.insert(std::make_pair(k2, "two"));
myMap.insert(std::make_pair(k3, "three"));
myMap.insert(std::make_pair(k4, "four"));
myMap.insert(std::make_pair(k1, "one.2"));
myMap.insert(std::make_pair(k4, "four.2"));
myMap.insert(std::make_pair(k5, "c1"));
myMap.insert(std::make_pair(k5, "c1.2"));
myMap.insert(std::make_pair(k6, "c2"));
myMap.insert(std::make_pair(k6, "c2.2"));
myMap.insert(std::make_pair(k7, "c3"));
myMap.insert(std::make_pair(k8, "c4"));
myMap.insert(std::make_pair(k9, "s1"));
myMap.insert(std::make_pair(k10, "s2"));
myMap.insert(std::make_pair(k11, "s3"));
myMap.insert(std::make_pair(k12, "s4"));
myMap.insert(std::make_pair(k12, "s4.2"));
myMap.insert(std::make_pair(k11, "s3.2"));
myMap.insert(std::make_pair(k10, "s2.2"));
myMap.insert(std::make_pair(k9, "s1.2"));
multiKeyMap::iterator pos;
for (pos = myMap.begin(); pos != myMap.end(); ++pos) {
std::cout << pos->first.strIdx() << " : " << pos->second
<<"\n";
}
return (0);
}
指针它
在编写了几个派生类之后,我很快发现这有助于成为一个模板,我的解决方案很快就会落实到位。
我怀疑可能有更好的方法来实现这一点,但这是我到目前为止所做的:
BASE
4 keys for key_base_c
0
1
2
3
base_error < base_error: 0 = pass
base_error < base_int: 1 = pass
base_int < base_char: 1 = pass
base_char < base_str: 1 = pass
base_error == base_error: 1 = pass
base_int == base_int: 1 = pass
base_char == base_char: 1 = pass
base_str == base_str: 1 = pass
base_error == base_int: 0 = pass
base_int == base_char: 0 = pass
base_char == base_str: 0 = pass
4 keys for key_int_2
1.1
1.2
1.3
1.4
base_int < i1: 0 = pass
i1 < base_int: 0 = pass
i1 < base_char: 1 = pass
base_char < i1: 0 = pass
i1 == i1: 1 = pass
i1 == base_int: 1 = pass
base_int == i1: 1 = pass
i1 == base_error: 0 = pass
base_error == i1: 0 = pass
i1 < i2: 1 = pass
i1 < i3: 1 = pass
i1 < i4: 1 = pass
4 keys for key_char
2.a
2.b
2.c
2.d
base_int < c1: 1 = pass
base_int == c1: 0 = pass
base_char < c1: 0 = pass
base_char == c1: 1 = pass
base_str < c1: 0 = pass
base_str == c1: 0 = pass
c1 < c1: 0 = pass
c1 == c1: 1 = pass
c1 < c2: 1 = pass
c1 == c2: 0 = pass
c1 == i1: 0 = pass
i1 == c1: 0 = pass
c1 < i1: 0 = pass
i1 < c1: 1 = pass
4 keys for key_str
3.aaa
3.bbb
3.ccc
3.ddd
base_int < s1: 1 = pass
base_char < s1: 1 = pass
base_str < s1: 0 = pass
base_str == s1: 1 = pass
s1 < base_int: 0 = pass
s1 < base_char: 0 = pass
s1 < base_str: 0 = pass
s1 == base_str: 1 = pass
s1 < s1: 0 = pass
s1 == s1: 1 = pass
s1 < s2: 1 = pass
s1 == s2: 0 = pass
NOW TESTING THE MAP
1.1 : one
1.1 : one.2
1.2 : two
1.3 : three
1.4 : four
1.4 : four.2
2.a : c1
2.a : c1.2
2.b : c2
2.b : c2.2
2.c : c3
2.d : c4
3.aaa : s1
3.aaa : s1.2
3.bbb : s2
3.bbb : s2.2
3.ccc : s3
3.ccc : s3.2
3.ddd : s4
3.ddd : s4.2
此输出为:
{{1}}