想象一下,您有一个包含一堆成员的结构,并且您想使用通过其中一个成员引用的特定值作为集合中的键,如下所示:
class ComplexClass {
public:
const string& name() const;
// tons of other stuff
};
struct MyStruct {
ComplexClass* c;
MoreStuff* x;
};
struct CmpMyStruct {
bool operator()(const MyStruct& lhs, const MyStruct& rhs) {
return lhs.c->name() < rhs.c->name();
}
};
typedef set<MyStruct, CmpMyStruct> MySet;
MySet my_set;
这工作得很好,但是现在我想通过字符串名称进行查找,但是 my_set.find() 现在当然需要一个“const MyStruct&”。如果名称不是从该 ComplexClass 中取出,而是 MyStruct 的成员,我可以快速伪造 MyStruct 的一个实例并使用它:
MyStruct tmp_for_lookup;
tmp_for_lookup.name = "name_to_search"; // Doesn't work of course
MySet::iterator iter = my_set.find(tmp_for_lookup);
但是,如前所述,它不是这样工作的,名称在 ComplexClass 中,所以我必须至少在其中放一个模拟或其他东西。
所以我真正想要的是 STL 集不会比较 MyStructs,而是首先从 MyStruct(具有字符串类型)中“投影”出密钥,然后对其进行操作,包括 find()。我开始深入研究 gcc 中 set/map 的实现,看看他们是如何为地图解决这个问题的,很遗憾地看到他们实际上在内部 _Rb_tree 中解决了它,但没有公开它,因为它不是标准。来自 gcc 的 stl_tree.h:
template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc = allocator<_Val> >
class _Rb_tree
{
....
template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
typename _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator
_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
find(const _Key& __k)
然后在 stl_map.h 中:
typedef _Rb_tree<key_type, value_type, _Select1st<value_type>,
key_compare, _Pair_alloc_type> _Rep_type;
请注意它如何使用 '_Select1st' 将键从 value_type 中投影出来,因此 find() 实际上可以只使用该键。另一方面, stl_set.h 在这种情况下只使用身份,正如预期的那样。
所以我想知道,有没有一种方法是我目前缺少的,因为我可以使用普通的 STL 集/地图实现相同的美感和效率(即我绝对不想直接使用 GCC 特定的 _Rb_tree),这样我真的可以做到
MySet::iterator iter = my_set.find("somestring");
请注意,我特别不想将 my_set 更改为从字符串到 MyStructs 的映射,即我不想将字符串(或对它的引用)从 ComplexClass 中复制出来,这样我就可以做map<string, MyStruct>
或map<const string&, MyStruct>
代替。
在这一点上,这几乎更像是一种思想练习,但看起来很有趣:)