web-dev-qa-db-ja.com

C ++ 11のunique_ptrのベクトル

最近C++ 11に切り替えて、そこでの優れたプラクティスに慣れようとしています。私が頻繁に扱うことになるのは、次のようなものです。

class Owner
{
private:
    vector<unique_ptr<HeavyResource>> _vectorOfHeavyResources;
public:
    virtual const vector<const HeavyResource*>* GetVectorOfResources() const;
};

これには、_returnableVectorを追加し、後で返すことができるようにソースベクトルを変換するようなことをする必要があります。

_returnableVector = vector<HeavyResource*>;
for (int i=0; i< _vectorOfHeavyResources.size(); i++)
{
    _returnableVector.Push_back(_vectorOfHeavyResources[i].get());
}

誰かが同様の問題に気づきましたか?あなたの考えと解決策は何ですか?私はここで所有権のアイデア全体を手に入れていますか?

更新:ここに別のことがあります:あるクラスが何らかの処理の結果をvector<unique_ptr<HeavyResource>>として返し(結果の所有権を呼び出し元に渡す)、それが後続の処理に使用されることになっている場合はどうなりますか?

vector<unique_ptr<HeavyResource>> partialResult = _processor1.Process();
// translation
auto result = _processor2.Process(translatedPartialResult); // the argument of process is vector<const HeavyResource*>
13
Witek

unique_ptrされていないベクトルを維持して返す代わりに、要素に直接アクセスする関数を提供することをお勧めします。これにより、リソースのストレージがカプセル化されます。クライアントは、それらがunique_ptrsとして保存されていることも、vectorに保持されていることも知りません。

これの1つの可能性は、boost::indirect_iteratorを使用してunique_ptrを自動的に逆参照することです。

using ResourceIterator =
     boost::indirect_iterator<std::vector<std::unique_ptr<HeavyResource>>::iterator,
                              const HeavyResource>;
ResourceIterator begin() { return std::begin(_vectorOfHeavyResources); }
ResourceIterator end() { return std::end(_vectorOfHeavyResources); }

デモ

16
TartanLlama

これに頻繁に遭遇する場合は、unique_ptrのように動作するが、ポインタの定数をそれが指すオブジェクトに渡すクラスを作成することは理にかなっているかもしれません。そうすれば、ベクトルへのconst参照を返すことができます。

私はこれを一度書いて、それで終わりました:

#include <iostream>
#include <vector>
#include <memory>

//unique,const-preserving pointer
template<class T>
class ucp_ptr {
    std::unique_ptr<T> ptr;
public:
    ucp_ptr() = default;
    ucp_ptr(T* ptr) :ptr{ ptr }{};
    ucp_ptr(std::unique_ptr<T>&& other) :ptr(std::move(other)){};

    T&        operator*()       { return ptr.get(); }
    T const & operator*()const  { return ptr.get(); }

    T*        operator->()      { return ptr.get(); }
    T const * operator->()const { return ptr.get(); }
};

struct Foo {
    int a = 0;
};

int main() {
    std::vector<ucp_ptr<Foo>> v;
    v.emplace_back(new Foo());
    v.emplace_back(std::make_unique<Foo>());    

    v[0]->a = 1;
    v[1]->a = 2;

    const std::vector<ucp_ptr<Foo>>& cv = v;

    std::cout << cv[0]->a << std::endl; //<-read access OK
    //cv[1]->a = 10; //<-compiler error
}

もちろん、カスタム削除機能が必要な場合や、配列を管理するための特殊化を追加したい場合は、少し拡張できますが、これは基本バージョンです。私はまた、ここSOのどこかでこのどこかでより洗練されたバージョンを見たと信じていますが、今はそれを見つけることができません。

これをクラスで使用する方法の例を次に示します。

class Bar {
    std::vector<ucp_ptr<Foo>> v;
public:
    void add(const Foo& foo){ 
        v.Push_back(std::make_unique<Foo>(foo)); 
    }
    //modifying elements
    void doubleElements() {
        for (auto& e : v){
            e->a *= 2;
        }
    }
    const std::vector<ucp_ptr<Foo>>& showElements() const{
        return v;
    }
};

編集

更新に関する限り、TをBに、またはその逆にキャストすることが有効であっても、vector<T>vector<B>とは無関係であるという事実に従わなければなりません。
(必要に応じて各要素をキャストすることで)要素に異なるビューを提供するアダプターを作成できますが、適切なタイプの新しいベクトルを作成する以外に、一般的なメカニズムは存在しません(私が知っていること)の)あなたがやりたいことをする。

1
MikeMB

代わりにshared_ptrを検討することをお勧めします。これは、おそらく達成しようとしていることを反映しているためです。小さなスコープでunique_ptrを使用したい場合や、オブジェクトを使用して1つだけを反映したい場合はいつでも使用できます。

0
Tas