使用带有 boost::shared_ptr 的基类在地图中查找

Posted

技术标签:

【中文标题】使用带有 boost::shared_ptr 的基类在地图中查找【英文标题】:Find in a map using the base class with a boost::shared_ptr 【发布时间】:2014-05-07 13:26:09 【问题描述】:

我正在寻找一种使用基类在地图中查找元素的方法(下面的代码只是一个基本示例):

#include <map>
#include <boost/shared_ptr.hpp>

class Base 
public:
    Base(int v) : id(v) ;
    int id;  
;

class Derived : public Base 
public:
    Derived(int v) : Base(v) ;
;


int main()

    std::map<boost::shared_ptr<Derived>, double> m;
    m.insert(std::make_pair(boost::shared_ptr<Derived>(new Derived(1)), 10));
    m.insert(std::make_pair(boost::shared_ptr<Derived>(new Derived(2)), 20));

    auto b1 = boost::shared_ptr<Base>(new Base(1));
    m.find(b1);

    return 0;

基本上,我想比较id 属性。编译器返回的错误如下:

main.cpp: In function 'int main()':
main.cpp:35:14: error: no matching function for call to 'std::map<boost::shared_ptr<Derived>, double>::find(boost::shared_ptr<Base>&)'
     m.find(b1);
              ^
main.cpp:35:14: note: candidates are:
In file included from /usr/include/c++/4.8/map:61:0,
                 from main.cpp:1:
/usr/include/c++/4.8/bits/stl_map.h:820:7: note: std::map<_Key, _Tp, _Compare, _Alloc>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const key_type&) [with _Key = boost::shared_ptr<Derived> _Tp = double; _Compare = std::less<boost::shared_ptr<Derived> > _Alloc = std::allocator<std::pair<const boost::shared_ptr<Derived>, double> > std::map<_Key, _Tp, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::pair<const boost::shared_ptr<Derived>, double> > std::map<_Key, _Tp, _Compare, _Alloc>::key_type = boost::shared_ptr<Derived>]
       find(const key_type& __x)
       ^
/usr/include/c++/4.8/bits/stl_map.h:820:7: note:   no known conversion for argument 1 from 'boost::shared_ptr<Base>' to 'const key_type& aka const boost::shared_ptr<Derived>&'
/usr/include/c++/4.8/bits/stl_map.h:835:7: note: std::map<_Key, _Tp, _Compare, _Alloc>::const_iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const key_type&) const [with _Key = boost::shared_ptr<Derived> _Tp = double; _Compare = std::less<boost::shared_ptr<Derived> > _Alloc = std::allocator<std::pair<const boost::shared_ptr<Derived>, double> > std::map<_Key, _Tp, _Compare, _Alloc>::const_iterator = std::_Rb_tree_const_iterator<std::pair<const boost::shared_ptr<Derived>, double> > std::map<_Key, _Tp, _Compare, _Alloc>::key_type = boost::shared_ptr<Derived>]
       find(const key_type& __x) const
       ^
/usr/include/c++/4.8/bits/stl_map.h:835:7: note:   no known conversion for argument 1 from 'boost::shared_ptr<Base>' to 'const key_type& aka const boost::shared_ptr<Derived>&'

【问题讨论】:

这里应该是Base共享指针的映射 @Kiroxas 谢谢,但我正在寻找一种使用 Base 共享指针搜索 Derivate 共享指针映射的方法。 【参考方案1】:

如果您想通过id 使用您的地图进行查找,您需要传入一个适当的比较函数,以便地图按id 而不是默认的operator &lt; 对其键进行排序(我相信,将所有权块地址与boost::shared_ptr 参数进行比较)。

所以像这样改变地图:

struct Less_id

  bool operator() (const boost::shared_ptr<Derived> &lhs, const boost::shared_ptr<Derived> &rhs) const
  
    return lhs->id < rhs->id;
  
;

typedef std::map<boost::shared_ptr<Derived>, double, Less_id> Map;

Map m;

这将对地图进行相应的排序,但仍不允许通过Base 指针进行查找。为此,您可以在std::lower_bound 上方编写自己的函数:

Map::const_iterator find_base(const Map &map, const boost::shared_ptr<Base> &base)

  auto it = std::lower_bound(
    map.begin(), map.end(), base,
    [](const Map::value_type &lhs, const boost::shared_ptr<Base> &rhs)
     return lhs.first->id < rhs->id; 
  );
  if (it != map.end() && it->first->id == base->id)
    return it;
  else
    return map.end();

std::lower_bound() 用于保持std::map::find() 提供的对数复杂度。

Live example

【讨论】:

【参考方案2】:

使用一个

std::map<boost::shared_ptr<Derived>, double, std::less<boost::shared_ptr<Base>>>

编辑:我领先于时代——这只适用于 C++14。

【讨论】:

问题是(至少在 C++14 出来之前),find() 需要一个 const key_type&amp;,所以它需要一个 boost::shared_ptr&lt;Derived&gt; 参数。【参考方案3】:

有两个问题需要克服。第一个是您想在Deriveds 的集合中搜索Base。第二个是您要按值而不是按地址进行比较。 其他答案忽略了第二点。试试std::find_if

auto b1 = boost::shared_ptr<Base>(new Base(1));
auto itFound = std::find_if(m.begin(), m.end()
   [=](const std::pair<boost::shared_ptr<Derived>, double>& pair)
   
      // omitting null checks for this example
      return pair.first->id == b1->id;
   );

如果要求真的只是找到具有给定 id 的密钥,你可以让它更简单:

int queryKey = 1;
auto itFound = std::find_if(m.begin(), m.end()
   [=](const std::pair<boost::shared_ptr<Derived>, double>& pair)
   
      return pair.first->id == queryKey;
   );

现在,正如 cmets 中所述,这将为您提供线性而不是 map 通常的对数查找时间。如果地图很小,则无关紧要,但如果这是一个问题,您可以使用std::lower_bound 而不是find_if。请注意,这还需要添加自定义比较器,以便您可以确保地图的排序顺序基于 id。例如:

struct Compare

   bool operator()(const boost::shared_ptr<Derived>& l,
      const boost::shared_ptr<Derived>& r) const
   
      // omitting null checks for this example
      return l->id < r->id;
   
;

std::map<boost::shared_ptr<Derived>, double, Compare> m;

【讨论】:

缺点是std::find_if 将简单地使用范围作为一个序列,而忽略它上面的查找结构——给出线性而不是对数的性能。 std::lower_bound 可能是更好的候选人。 @Angew 是的。已编辑。【参考方案4】:

这是因为 boost::shared_ptr 的操作符

使用 std::map<_key _tp _compare>

例如:

 std::map<boost::shared_ptr<Derived>, double, compare_func()> m;

【讨论】:

好的,但问题是:如何使用 shared_ptr 实现一个 compare_func 来搜索 shared_ptr 的映射。【参考方案5】:

当您正在寻找一个对象Base,它只能在地图中,如果它是Derived 类型,您可以简单地这样做:

boost::shared_ptr<Derived> d1 = boost::dynamic_pointer_cast<Derived>(b1);
if(d1) 
   m.find(d1);
 else 
   // not in the map


当您使用std::shared_ptr&lt;Derived&gt; 作为时,另一种可能性是实际使用指向基类的指针:

使用

std::map<boost::shared_ptr<Base>, double> m;

而不是

std::map<boost::shared_ptr<Derived>, double> m;

一切都按预期进行。


顺便说一句,您在Base 中缺少virtual 析构函数!

【讨论】:

代码只是一个基本的例子,不幸的是我无法更改地图键。 @HugoJuarezCorrá:我提供了另一个不需要更改地图键的解决方案。

以上是关于使用带有 boost::shared_ptr 的基类在地图中查找的主要内容,如果未能解决你的问题,请参考以下文章

使用 boost::shared_ptr 访问静态成员变量

boost: 使用自定义删除器序列化 shared_ptr

如何使用 boost::shared_ptr 构造二叉树

Boost智能指针——shared_ptr

返回 boost::shared_ptr 和从返回的原始指针构造 boost::shared_ptr 有啥区别?

boost::shared_ptr与定制删除器