对unique_ptrs集的原始指针查找

Jo *_*tes 40 c++ unique-ptr c++11 c++14

我经常发现自己想写这样的代码:

class MyClass
{
public:
  void addObject(std::unique_ptr<Object>&& newObject);

  void removeObject(const Object* target);

private:
  std::set<std::unique_ptr<Object>> objects;
};
Run Code Online (Sandbox Code Playgroud)

但是,很多std :: set接口对std :: unique_ptrs都没用,因为查找函数需要std :: unique_ptr参数(我显然没有这些参数,因为它们由集合本身拥有).

我可以想到两个主要的解决方案.

  1. 创建临时unique_ptr以进行查找.例如,上面的removeObject()可以实现如下:

    void MyClass::removeObject(const Object* target)
    {
      std::unique_ptr<Object> targetSmartPtr(target);
      objects.erase(targetSmartPtr);
      targetSmartPtr.release();
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 将原始指针映射替换为unique_ptrs.

      // ...
      std::map<const Object*, std::unique_ptr<Object>> objects;
    };
    
    Run Code Online (Sandbox Code Playgroud)

然而,对我来说,两者似乎都有点愚蠢.在解决方案1中,erase()不是noexcept,因此临时unique_ptr可能会删除它实际上不拥有的对象,而2需要不必要地为容器存储两倍.

我知道Boost的指针容器,但与现代C++ 11标准库容器相比,它们目前的功能有限.

我最近在阅读有关C++ 14的内容,并且遇到了"将异构比较查找添加到关联容器".但是形成我对它的理解,查找类型必须与键类型相当,但原始指针不能与unique_ptrs相比.

任何人都知道更优雅的解决方案或即将添加的C++解决了这个问题?

Yak*_*ont 32

C++ 14中,如果存在std::set<Key>::find则是一个template函数Compare::is_transparent.传入的类型不需要Key,在比较器下等效.

所以写一个比较器:

template<class T>
struct pointer_comp {
  typedef std::true_type is_transparent;
  // helper does some magic in order to reduce the number of
  // pairs of types we need to know how to compare: it turns
  // everything into a pointer, and then uses `std::less<T*>`
  // to do the comparison:
  struct helper {
    T* ptr;
    helper():ptr(nullptr) {}
    helper(helper const&) = default;
    helper(T* p):ptr(p) {}
    template<class U, class...Ts>
    helper( std::shared_ptr<U,Ts...> const& sp ):ptr(sp.get()) {}
    template<class U, class...Ts>
    helper( std::unique_ptr<U, Ts...> const& up ):ptr(up.get()) {}
    // && optional: enforces rvalue use only
    bool operator<( helper o ) const {
      return std::less<T*>()( ptr, o.ptr );
    }
  };
  // without helper, we would need 2^n different overloads, where
  // n is the number of types we want to support (so, 8 with
  // raw pointers, unique pointers, and shared pointers).  That
  // seems silly:
  // && helps enforce rvalue use only
  bool operator()( helper const&& lhs, helper const&& rhs ) const {
    return lhs < rhs;
  }
};
Run Code Online (Sandbox Code Playgroud)

然后使用它:

typedef std::set< std::unique_ptr<Foo>, pointer_comp<Foo> > owning_foo_set;
Run Code Online (Sandbox Code Playgroud)

现在,owning_foo_set::find将接受unique_ptr<Foo>Foo*shared_ptr<Foo>(或任何派生类Foo)并找到正确的元素.

在C++ 14之外,你被迫使用mapto unique_ptr方法或类似的东西,因为签名find过于严格.或者写自己的set等价物.

  • +1.显然,没有明显的妥协,没有办法用标准的C++ 11容器做到这一点.在最坏的情况下,某些东西将以线性时间运行,或者OP中提到的那些变通方法. (2认同)