如何使用不同类型的键搜索 std::map
How can I search an std::map using a key of a different type
如果我有 std::map<X, Blah>
,使用 Y
实例在地图中查找匹配项的最佳方法是什么?
假设 Y
中的信息足以唯一地找到 X
,但出于性能原因,我不想通过复制 [=13] 创建 X
的实例=] 值。
我意识到我可以通过为 X
和 Y
创建一个公共基础 class 或接口并使其成为映射键来做到这一点,但是还有其他方法吗?例如创建某种比较器对象?
为清楚起见,这里是示例代码:
class X
{
public:
int id;
int subId;
};
std::map<X, Details> detailsMap;
class Y
{
public:
int getId();
int getSubId();
int someOtherUnrelatedThings1;
int someOtherUnrelatedThings2;
};
现在,如果我有一个 Y
的实例,原则上我应该能够在我的地图中找到匹配的项目,因为我可以获得 id
和 subId
对.但是我可以不创建 X
的实例并复制 id
和 subId
吗?
C++14 添加了 is_transparent
对 map
排序的支持。
struct compare_helper {
X const* px = nullptr;
Y const* py = nullptr;
compare_helper(compare_helper&&)=default;
compare_helper(X const& x):px(&x) {}
compare_helper(Y const& y):py(&y) {}
explicit operator bool()const{return px&&py;}
friend bool operator<(compare_helper&& lhs, compare_helper&& rhs) {
if (!lhs || !rhs) {
return !rhs < !lhs;
}
// TODO: compare lhs and rhs based off px and py
}
};
struct ordering_helper {
using is_transparent=std::true_type;
bool operator()(compare_helper lhs, compare_helper rhs)const{
return std::move(lhs)<std::move(rhs);
}
};
现在重新定义你的 std::map
:
std::map<X, Details, ordering_helper> detailsMap;
大功告成。您现在可以将 Y const&
传递给 detailsMap.find
或其他任何东西。
现在// TODO: compare lhs and rhs based off px and py
有点烦人。
但应该是可写的。
如果您需要许多不同的 class 才能与 X
进行比较,并且您需要保存每个 compare_helper
class,或者您需要以某种方式键入擦除操作。
基本上,compare_helper
需要存储一个指向 X
的指针,或者一个 std::function< int(X const&) >
来告诉你 X
是否小于、等于、或大于其他参数。 (你会注意到在比较 Y
与 Y
或 Z
与 Y
时失败——在这种情况下, returning false 应该安全起见,因为在给定的地图搜索中您只会看到一个非 X
。
我们可以通过一些 ADL 将其与 compare_helper
的定义分离:
struct compare_helper {
X const* px = nullptr;
using f_helper = std::function< int(X const&) >;
f_helper not_X;
compare_helper(compare_helper&&)=default;
compare_helper(X const& x):px(std::addressof(x)) {}
template<class NotX,
class=std::enable_if_t< std::is_convertible<
decltype(compare_with_X( std::forward<NotX>(notx) ))
, f_helper
>{}
>
compare_helper( NotX&& notx ):
not_X( compare_with_X( std::forward<NotX>(notx) ) )
{}
explicit operator bool()const{return px&¬_X;}
friend bool operator<(compare_helper&& lhs, compare_helper&& rhs) {
if (!lhs || !rhs) {
return !rhs < !lhs;
}
if (lhs.px && rhs.px) { return *lhs.px < *rhs.px; }
if (lhs.px && rhs.not_X) { return rhs.not_X(*lhs.px) < 0; }
if (lhs.not_X && rhs.px) { return lhs.not_X(*rhs.px) > 0; }
else return false;
}
};
现在,最终用户只需将要与 X
比较的类型的命名空间中的自由函数 compare_with_X
覆盖为 return 和 std::function<int(X const&)>
,并且上面的地图允许从您的非 X
类型进行查找。
使用 C++14,您可以使用异构查找。
如果你想找到一个元素,其键将 等价 与 std::map::find
的参数进行比较,你应该提供一个比较器作为第三个模板参数应该将 Comparator::is_transparent
表示为一种类型。它还应包含 bool operator()
将您的地图键与您喜欢的任何其他类型进行比较。
撇开有趣的描述不谈,这里有一个例子:
struct X
{
int id;
int subid;
};
struct Details {};
struct Comparator
{
using is_transparent = std::true_type;
// standard comparison (between two instances of X)
bool operator()(const X& lhs, const X& rhs) const { return lhs.id < rhs.id; }
// comparison via id (compares X with integer)
bool operator()(const X& lhs, int rhs) const { return lhs.id < rhs; }
bool operator()(int lhs, const X& rhs) const { return lhs < rhs.id; }
// Same thing with Y
bool operator()(const X& lhs, const Y& rhs) const { return lhs.id < rhs.getId(); }
bool operator()(const Y& lhs, const X& rhs) const { return lhs.getId() < rhs.id; }
};
int main()
{
std::map<X, Details, Comparator> detailsMap = {
{ X{1, 2}, Details{} },
{ X{3, 4}, Details{} },
{ X{5, 6}, Details{} }
};
// it1 and it2 point to the same element.
auto it1 = detailsMap.find(X{1, 2});
auto it2 = detailsMap.find(1);
std::cout << detailsMap.size() << std::endl;
std::cout << std::boolalpha << (it1 == detailsMap.end()) << std::endl; // false
std::cout << std::boolalpha << (it1 == it2) << std::endl; // true
}
但是请注意,GCC 直到修订版 219888 才实现它。
如果我有 std::map<X, Blah>
,使用 Y
实例在地图中查找匹配项的最佳方法是什么?
假设 Y
中的信息足以唯一地找到 X
,但出于性能原因,我不想通过复制 [=13] 创建 X
的实例=] 值。
我意识到我可以通过为 X
和 Y
创建一个公共基础 class 或接口并使其成为映射键来做到这一点,但是还有其他方法吗?例如创建某种比较器对象?
为清楚起见,这里是示例代码:
class X
{
public:
int id;
int subId;
};
std::map<X, Details> detailsMap;
class Y
{
public:
int getId();
int getSubId();
int someOtherUnrelatedThings1;
int someOtherUnrelatedThings2;
};
现在,如果我有一个 Y
的实例,原则上我应该能够在我的地图中找到匹配的项目,因为我可以获得 id
和 subId
对.但是我可以不创建 X
的实例并复制 id
和 subId
吗?
C++14 添加了 is_transparent
对 map
排序的支持。
struct compare_helper {
X const* px = nullptr;
Y const* py = nullptr;
compare_helper(compare_helper&&)=default;
compare_helper(X const& x):px(&x) {}
compare_helper(Y const& y):py(&y) {}
explicit operator bool()const{return px&&py;}
friend bool operator<(compare_helper&& lhs, compare_helper&& rhs) {
if (!lhs || !rhs) {
return !rhs < !lhs;
}
// TODO: compare lhs and rhs based off px and py
}
};
struct ordering_helper {
using is_transparent=std::true_type;
bool operator()(compare_helper lhs, compare_helper rhs)const{
return std::move(lhs)<std::move(rhs);
}
};
现在重新定义你的 std::map
:
std::map<X, Details, ordering_helper> detailsMap;
大功告成。您现在可以将 Y const&
传递给 detailsMap.find
或其他任何东西。
现在// TODO: compare lhs and rhs based off px and py
有点烦人。
但应该是可写的。
如果您需要许多不同的 class 才能与 X
进行比较,并且您需要保存每个 compare_helper
class,或者您需要以某种方式键入擦除操作。
基本上,compare_helper
需要存储一个指向 X
的指针,或者一个 std::function< int(X const&) >
来告诉你 X
是否小于、等于、或大于其他参数。 (你会注意到在比较 Y
与 Y
或 Z
与 Y
时失败——在这种情况下, returning false 应该安全起见,因为在给定的地图搜索中您只会看到一个非 X
。
我们可以通过一些 ADL 将其与 compare_helper
的定义分离:
struct compare_helper {
X const* px = nullptr;
using f_helper = std::function< int(X const&) >;
f_helper not_X;
compare_helper(compare_helper&&)=default;
compare_helper(X const& x):px(std::addressof(x)) {}
template<class NotX,
class=std::enable_if_t< std::is_convertible<
decltype(compare_with_X( std::forward<NotX>(notx) ))
, f_helper
>{}
>
compare_helper( NotX&& notx ):
not_X( compare_with_X( std::forward<NotX>(notx) ) )
{}
explicit operator bool()const{return px&¬_X;}
friend bool operator<(compare_helper&& lhs, compare_helper&& rhs) {
if (!lhs || !rhs) {
return !rhs < !lhs;
}
if (lhs.px && rhs.px) { return *lhs.px < *rhs.px; }
if (lhs.px && rhs.not_X) { return rhs.not_X(*lhs.px) < 0; }
if (lhs.not_X && rhs.px) { return lhs.not_X(*rhs.px) > 0; }
else return false;
}
};
现在,最终用户只需将要与 X
比较的类型的命名空间中的自由函数 compare_with_X
覆盖为 return 和 std::function<int(X const&)>
,并且上面的地图允许从您的非 X
类型进行查找。
使用 C++14,您可以使用异构查找。
如果你想找到一个元素,其键将 等价 与 std::map::find
的参数进行比较,你应该提供一个比较器作为第三个模板参数应该将 Comparator::is_transparent
表示为一种类型。它还应包含 bool operator()
将您的地图键与您喜欢的任何其他类型进行比较。
撇开有趣的描述不谈,这里有一个例子:
struct X
{
int id;
int subid;
};
struct Details {};
struct Comparator
{
using is_transparent = std::true_type;
// standard comparison (between two instances of X)
bool operator()(const X& lhs, const X& rhs) const { return lhs.id < rhs.id; }
// comparison via id (compares X with integer)
bool operator()(const X& lhs, int rhs) const { return lhs.id < rhs; }
bool operator()(int lhs, const X& rhs) const { return lhs < rhs.id; }
// Same thing with Y
bool operator()(const X& lhs, const Y& rhs) const { return lhs.id < rhs.getId(); }
bool operator()(const Y& lhs, const X& rhs) const { return lhs.getId() < rhs.id; }
};
int main()
{
std::map<X, Details, Comparator> detailsMap = {
{ X{1, 2}, Details{} },
{ X{3, 4}, Details{} },
{ X{5, 6}, Details{} }
};
// it1 and it2 point to the same element.
auto it1 = detailsMap.find(X{1, 2});
auto it2 = detailsMap.find(1);
std::cout << detailsMap.size() << std::endl;
std::cout << std::boolalpha << (it1 == detailsMap.end()) << std::endl; // false
std::cout << std::boolalpha << (it1 == it2) << std::endl; // true
}
但是请注意,GCC 直到修订版 219888 才实现它。