TL; DR:我有一个用例,其中重要的是调用WidgetEqualTo()(new_widget, widget_inside_container)
或WidgetEqualTo()(widget_inside_container, new_widget)
。
可以多次重新创建相同的小部件,因此我有WidgetPool
(为了本示例的目的,是std::vector<const Widget*>
的全局包装器)和智能构造函数:
const Widget* combine(const Widget* a, const Widget* b) {
static std::unordered_map<std::pair<int, int>, int> cache;
std::pair<int, int> ab = std::make_pair(a->id(), b->id());
const auto it = cache.find(ab);
if (it == cache.end()) {
// The Widget ctor sets this->id() to WidgetPool::size()
// and appends this to WidgetPool.
const Widget* result = new Widget(a, b);
cache[ab] = result->id();
return result;
} else {
return WidgetPool::get_widget(it->second);
}
}
我还有一个容器,其中Widgets按其创建顺序插入。比方说,std::unordered_set<const Widget*, WidgetHash, WidgetEqualTo>
,WidgetEqualTo
看起来像这样:
struct WidgetEqualTo {
bool operator()(const Widget* a, const Widget* b) const {
if (a == b) {
return true;
}
// My Widgets obey the associative law:
// tedious_comparison(new Widget(new Widget(p, q), r),
// new Widget(p, new Widget(q, r))) == true.
const bool are_equal = tedious_comparison(a, b);
if (are_equal) {
// Cache the result of the comparison.
// Retain the older Widget.
if (a->id() < b->id()) { // (***)
WidgetPool::set_widget(b->id(), a);
delete b;
} else {
WidgetPool::set_widget(a->id(), b);
delete a;
}
}
return are_equal;
}
};
如果始终使用WidgetEqualTo()
调用(new_element, element_already_inside_unordered_set)
或相反,我可以删除标有(***)
的测试的一个分支。 FWIW,libstdc ++似乎调用了WidgetEqualTo()(new_element, old_element)
。 C ++标准是否保证了这种行为?
答案 0 :(得分:11)
没有
[C++11: 25.2.5/3]:
每个无序关联容器由Key
参数化,由符合Hash
要求(17.6.3.4)的函数对象类型Hash
参数化并充当哈希函数用于Key
类型的参数值,以及二元谓词Pred
,它可以在类型Key
的值上引发等价关系。此外,unordered_map
和unordered_multimap
将任意映射类型T
与Key
相关联。
表17告诉我们EqualityComparable
要求:
==
是等价关系,也就是说,它具有以下属性:
- 适用于所有
a
,a == a
。- 如果
a == b
,则b == a
。- 如果
a == b
和b == c
,则a == c
。
(gah!逗号拼接!)
请注意,比较器的给定语义没有提到操作数的具体方式:
[C++11: 25.2.5/5]:
如果容器的k1
函数对象在传递k2
时返回Key
,那么类型key_equal
的两个值true
和{{1}}将被视为等效值。 [..]
简单地说,如果提供参数的顺序很重要,那么你的程序会有未定义的行为。
这也不是C ++的奇怪之处; equivalence implies symmetry throughout mathematics