47

次のようなコードを書きたいと思うことがよくあります。

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

  void removeObject(const Object* target);

private:
  std::set<std::unique_ptr<Object>> objects;
};

ただし、ルックアップ関数には std::unique_ptr パラメーターが必要であるため、 std::set インターフェイスの多くは std::unique_ptrs では役に立たないものです (セット自体が所有しているため、これは明らかにありません)。

これには主に 2 つの解決策が考えられます。

  1. ルックアップ用の一時的な unique_ptr を作成します。たとえば、上記の removeObject() は次のように実装できます。

    void MyClass::removeObject(const Object* target)
    {
      std::unique_ptr<Object> targetSmartPtr(target);
      objects.erase(targetSmartPtr);
      targetSmartPtr.release();
    }
    
  2. セットを、unique_ptrs への生のポインターのマップに置き換えます。

      // ...
      std::map<const Object*, std::unique_ptr<Object>> objects;
    };
    

しかし、どちらも私には少しばかげているようです。解決策 1 では、erase() は noexcept ではないため、一時的な unique_ptr が実際には所有していないオブジェクトを削除する可能性があり、2 ではコンテナ用のストレージが不必要に 2 倍必要になります。

Boost のポインター コンテナーについては知っていますが、現在の機能は最新の C++11 標準ライブラリ コンテナーに比べて制限されています。

私は最近 C++14 について読んでいて、「異種比較ルックアップを連想コンテナーに追加する」に出くわしました。しかし、私の理解では、ルックアップ型はキー型に匹敵する必要がありますが、生のポインターはunique_ptrsに匹敵しません。

この問題を解決する、より洗練されたソリューションまたは C++ への今後の追加を知っている人はいますか?

4

6 に答える 6

38

C++14では、存在する場合std::set<Key>::findtemplate関数です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;
  }
};

それを使用します:

typedef std::set< std::unique_ptr<Foo>, pointer_comp<Foo> > owning_foo_set;

orまたは(または の派生クラス)owning_foo_set::findを受け入れ、正しい要素を見つけます。unique_ptr<Foo>Foo*shared_ptr<Foo>Foo

C++14 以外では、 の署名が過度に制限的であるため、 maptoアプローチまたは同等のものを使用する必要があります。または、独自の同等のものを作成します。unique_ptrfindset

于 2013-09-22T05:18:49.013 に答える
-1

ここではユニークなピンターを使用しています。これは、セットがオブジェクトの一意の所有権を持っていることを意味します。これは、オブジェクトが存在する場合、それがセット内にあるか、一意のポインターを持っていることを意味するはずです。この場合、セットを検索する必要さえありません。

しかし、私にはそうではないように見えます。この場合、共有ポインタを使用したほうがよいと思います。このセット以外の誰かがそれらを明確に保存しているため、共有ポインターを保存して渡すだけです。

于 2013-09-22T04:23:53.173 に答える