如何使用不同类型的键搜索std :: map

时间:2015-08-10 15:38:00

标签: c++ performance dictionary stl comparator

如果我有std::map<X, Blah>,使用Y的实例在地图中查找匹配项目的最佳方法是什么?

假设Y中的信息足以唯一地找到X,但出于性能原因,我不想通过复制X来创建Y的实例值。

我意识到我可以通过为XY创建公共基类或接口并使其成为地图键来实现此目的,但还有其他方法吗?例如创建某种比较对象?

为清晰起见,以下是示例代码:

class X
{
public:
    int id;
    int subId;
};

std::map<X, Details> detailsMap;

class Y
{
public:
    int getId();
    int getSubId();
    int someOtherUnrelatedThings1;
    int someOtherUnrelatedThings2;
};

现在,如果我有一个Y的实例,原则上我应该能够在我的地图中找到匹配的项目,因为我可以获得idsubId对。但是,如果不创建X的实例并复制idsubId,我可以这么做吗?

2 个答案:

答案 0 :(得分:9)

使用C ++ 14,您可以使用异构查找。

如果你想找到一个带有键的元素,将等效std::map::find的参数进行比较,你应该提供一个比较器作为第三个模板参数,它应该有{{1表示为一种类型。它还应包含Comparator::is_transparent,将您的地图密钥与您喜欢的任何其他类型进行比较。

除了有趣的描述,这是一个例子:

bool operator()

但请注意,GCC在修订219888之前没有实施。

答案 1 :(得分:0)

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有点烦人。

但它应该是可写的。

如果您需要许多不同的类来与X进行比较,并且您需要一个大的compare_helper类,每个类都保存,或者您需要以某种方式键入 - 擦除操作。

基本上,compare_helper需要存储指针 - Xstd::function< int(X const&) >,它会告诉您X是否小于,等于或大于比其他参数。 (你会注意到将YYZY进行比较时失败 - 在这种情况下,返回false应该是安全的,因为你只会在给定的地图搜索中看到一个非X

我们可以将这与compare_helper的定义与某些ADL分开:

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&&not_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;
  }
};

现在,最终用户只需覆盖要与compare_with_X进行比较的类型的命名空间中的自由函数X,以返回std::function<int(X const&)>,并且上面的地图允许从非X类型中查找。