8583


私は今、unique_ptrを用いてメモリ管理を行っています。  
その際、unique_ptrで配列を動的確保し、それをforeach文で回す方法があるかどうか知りたいです。

具体的には、

[code]

    unique_ptr < int[] > foo = make_unique< int[] >(20);
    for(auto& a : foo){
        a = 0;
    }
    
[/code]

のような感じです。  
unique_ptrにはイテレータが用意されていないので、私の知識ではこのような処理を記述することができませんが、配列の要素数を気にせず使えるので、出来ればforeach文で処理したいと考えています。  
C++はまだ１年も触ってない初心者ですので、解りにくい表現や、間違えて認識している部分を多々あるとは思いますが、どなたかご教授願います。


CC BY-SA 3.0
2015-03-30T14:56:52.450
8577
2015-04-05T17:34:17.960
2015-03-30T15:20:32.247
3639
9074
question
c++ c++11
unique_ptr< T[] >をforeach文で使用する方法をご教授お願いします。


`unique_ptr`でなく`vector`を使いましょう。

[code]

    vector<int> foo(20);
    for(auto& a : foo) {
        a = 0;
    }
    
[/code]


CC BY-SA 3.0
2015-03-30T15:18:19.297
8578
2015-03-30T15:18:19.297
3639
8577
answer


> unique_ptrで配列を動的確保し、それをforeach文で回す方法があるかどうか知りたいです。

ありません。

（ちなみに、C++には"foreach文"は存在しないので、range-based
for構文か[`std::for_each`アルゴリズム](http://cpprefjp.github.io/reference/algorithm/for_each.html)ですね。）

スマートポインタ`unique_ptr<T[]>`には配列要素数を管理する機構がないため、別変数を使って要素数を管理しなければなりません。また、要素走査には古典的forループが必要です。

[code]

    size_t num = 20;
    unique_ptr<int[]> foo = make_unique<int[]>(num);
    for(size_t i = 0; i < num; ++i){
        auto& a = foo[i];
        a = 0;
    }
    
[/code]

なので、通常は可変長配列`vector<T>`の利用をお勧めします。

* * *

おまけ：C++ランタイムライブラリは、配列の解放処理`delete[]`を正しく行うために、[その要素数を知っています](https://isocpp.org/wiki/faq/freestore-
mgmt#num-elems-in-new-array)。ただし、プログラムからこの情報にアクセスする方法は存在しません。


CC BY-SA 3.0
2015-03-31T00:26:52.707
8583
2015-03-31T00:26:52.707
49
8577
answer


yohjpさんの述べているとおり`unique_ptr`では要素数を知る方法がないため、別途要素数を自分で保存しておくことで実現することになります。要素数が分かれば、ラッパークラスを作ることで対応可能です。

以下のコードでは、そのようなラッパーとしてクラステンプレート`boost::iterator_range`およびそれを作成するヘルパ関数`boost::make_iterator_range`を使っています。

[code]

    #include <iostream>
    #include <memory>
    #include <boost/range/iterator_range.hpp>
    
    int main()
    {
        int n = 20;
        auto foo = std::make_unique<int[]>(n);
        for (auto& a : boost::make_iterator_range(foo.get(), foo.get() + n))
        {
            a = 0;
        }
    }
    
[/code]

Boostライブラリを使えなかったり使いたくなかったりする場合には、自作することになります。メンバ関数`begin`および`end`が存在するだけでよいので、比較的作成は容易だと思います。


CC BY-SA 3.0
2015-04-01T16:12:40.300
8663
2015-04-01T16:12:40.300
8616
8577
answer


他の方も言っているように、使用するクラスを変えるか、ラッパーを作成する。  
boostが使えない or どのように作るか知りたい場合の為、C++標準でどのように書けるか、最低限のサンプルのつもりで置いておく。

[code]

    #include <iostream>
    #include <memory>
    
    
    // unique_wrapperとmake_unique_wrapperを作るためのヘルパ - かなり手抜きその1.
    // 配列か判定する - 結果はvalueが1 or 0で判別.
    template <class vType> struct is_array {
        enum { value = 0 }; // falseである.
    };
    template <class vType> struct is_array<vType[]> {
        enum { value = 1 }; // trueである.
    };
    // trueのときだけ型が存在する - SFINAEに用いる.
    template <bool Condition, class vType>
    struct condition_type {};
    template <class vType>
    struct condition_type<true, vType> {
        using type = vType;
    };
    // 型から配列要素を抜く - 配列じゃなければ元のままに.
    template <class vType>
    struct remove_array {
        using type = vType;
    };
    template <class vType>
    struct remove_array<vType[]> {
        using type = vType;
    };
    
    // 通常版と配列版を同時にラッピングしている分、若干危険かもしれない･･･？.
    template <class vType>
    class unique_wrapper : public std::unique_ptr<vType> {
        std::size_t num_array;
        using MyType = unique_wrapper<vType>;
        using PtrType = typename remove_array<vType>::type;
    public:
        unique_wrapper(MyType const &) = delete;
        // 1個版と配列版と両方のコンストラクタを持つ - make_unique_wrapperのfriendにしてprivate化するか？.
        unique_wrapper(PtrType *ptr) : std::unique_ptr<vType>(ptr), num_array(1){}
        unique_wrapper(PtrType *ptr, std::size_t num) : std::unique_ptr<vType>(ptr), num_array(num){}
        unique_wrapper(MyType &&rhs) : std::unique_ptr<vType>(rhs.release()), num_array(rhs.num_array){}
    
        // 最低限このようなインターフェースを持つiteratorと
        class iterator {
            PtrType *ptr_;
        public:
            iterator(PtrType *ptr) : ptr_(ptr){}
            ~iterator(){}
    
            PtrType *operator ++(){ ++ptr_; return ptr_; }
            PtrType &operator *(){ return *ptr_; }
            bool operator !=(iterator rhs){ return *ptr_ != *rhs; }
        };
        // 上記を戻すbeginとendというインターフェースがあればrange-based forは動く.
        iterator begin(){ return this->get(); }
        iterator end(){ return this->get() + num_array; }
    };
    
    // make_unique_wrapper自体の各種 - かなり手抜きその2.
    template <class vType>
    unique_wrapper<typename condition_type<is_array<vType>::value == 1, vType>::type> make_unique_wrapper(std::size_t n){
        unique_wrapper<vType> wrap(new typename remove_array<vType>::type[n], n);
        return wrap;
    }
    template <class vType, class ...Args>
    unique_wrapper<typename condition_type<is_array<vType>::value == 0, vType>::type> make_unique_wrapper(Args... args){
        unique_wrapper<vType> wrap(new vType((args)...));
        return wrap;
    }
    
    
    struct Foo {
        ~Foo(){
            std::cout << "Foo destructor." << std::endl;
        }
        void print(){
            std::cout << "My name is Foo." << std::endl;
        }
    };
    
    
    int main()
    {
        // use test code.
        {
            unique_wrapper<int[]> ptr = make_unique_wrapper<int[]>(7);
            {
                for (auto &n : ptr){
                    std::cout << "Please number and enter : ";
                    std::cin >> n;
                }
            }
    
            for (auto n : ptr){
                std::cout << n << std::endl;
            }
            std::cin.get();
            std::cin.get();
        }   // この段階でちゃんとdelete[]が働く.
        {
            // 折角なんでFooさんでテスト.
            unique_wrapper<Foo> ptr = make_unique_wrapper<Foo>();
            ptr->print();
        }
    
        std::cin.get();
    }
    
[/code]

※コード書き直した。前の状態だと、破棄する場面で正しく破棄できない(配列版unique_ptrが使用されない)のに、make_unique_wrapperで指定できるのは配列作成のみだったのでちぐはぐな状態だった。  
書き直した現状でも、想定通りのmake_unique_wrapperやmake_unique_wrapper以外の書き方への正しいエラー化を行ってはおらず、あくまで知識のたたき台として用意。  
大事なのは、最低限あれだけのインターフェース(iteratorのoperatorとunique_wrapperのbeginとend)を作りさえすればrange-
based-forに投げ込めるという部分である。


CC BY-SA 3.0
2015-04-05T13:17:21.437
8793
2015-04-05T17:34:17.960
2015-04-05T17:34:17.960
8675
8675
8577
answer