web-dev-qa-db-ja.com

動的配列の範囲ベースのforループ?

次の構文の範囲ベースのforループがあります。

for(auto& i : array)

次のように、定数配列では機能しますが、ポインタベースの動的配列では機能しません。

int *array = new int[size];
for(auto& i : array)
   cout<< i << endl;

たとえば、置換の失敗に関するエラーと警告が表示されます。

エラー] C:\ Users\Siegfred\Documents\C-Free\Temp\Untitled2.cpp:16:16:エラー: 'begin(int *&)'の呼び出しに一致する関数がありません

動的配列でこの新しい構文を使用するにはどうすればよいですか?

23

範囲ベースのforループを使用するには、begin()およびend()メンバー関数を提供するか、非メンバーbegin()およびend()関数。後者の場合、_std::pair_で範囲をラップし、begin()およびend()をオーバーロードできます。

_    namespace std {
        template <typename T> T* begin(std::pair<T*, T*> const& p)
        { return p.first; }
        template <typename T> T* end(std::pair<T*, T*> const& p)
        { return p.second; }
    }
_

これで、次のようにforループを使用できます。

_    for (auto&& i : std::make_pair(array, array + size))
        cout << i << endl;
_

stdも名前空間pairに存在するため、非メンバーのbegin()およびend()関数は、ここでstd名前空間でオーバーロードする必要があることに注意してください。標準の名前空間を改ざんしたくない場合は、独自の小さなペアクラスを作成し、名前空間にbegin()end()をオーバーロードします。

または、動的に割り当てられた配列の周りに薄いラッパーを作成し、begin()およびend()メンバー関数を提供します。

_    template <typename T>
    struct wrapped_array {
        wrapped_array(T* first, T* last) : begin_ {first}, end_ {last} {}
        wrapped_array(T* first, std::ptrdiff_t size)
            : wrapped_array {first, first + size} {}

        T*  begin() const noexcept { return begin_; }
        T*  end() const noexcept { return end_; }

        T* begin_;
        T* end_;
    };

    template <typename T>
    wrapped_array<T> wrap_array(T* first, std::ptrdiff_t size) noexcept
    { return {first, size}; }
_

そして、呼び出しサイトは次のようになります。

_    for (auto&& i : wrap_array(array, size))
         std::cout << i << std::endl;
_

24
user2218982

コンパイラはこの配列の開始と終了を推定できないため、動的に割り当てられた配列でrange-for-loopを使用することはできません。代わりに、常にコンテナを使用する必要があります。例 std::vector

std::vector<int> v(size);
for(const auto& elem: v)
    // do something
16
awesoon

最初の要素へのポインタしかないため、動的に割り当てられた配列に対して範囲ベースのループを直接実行することはできません。コンパイラがループを実行するために使用できるサイズに関する情報はありません。慣用的なC++ソリューションは、動的に割り当てられた配列をstd::vector

std::vector<int> arr(size);
for(const auto& i : arr)
  std::cout<< i << std::endl;

または、ポインターとオフセットに基づいて開始と終了のイテレーターを提供する範囲タイプを使用することもできます。 boost.range ライブラリーのタイプの一部、または [〜#〜] gsl [〜#〜] スパンの提案(実装例- ここ 、C++ 20提案タイプの参照 ここ )。


Forループに基づく範囲はstd::array修正のオブジェクトサイズのプレーン配列:

std::array<int,10> arr;
for(const auto& i : arr)
  std::cout<< i << std::endl;

int arr[10] = .... ;
for(const auto& i : arr)
  std::cout<< i << std::endl;

ただし、どちらの場合も、サイズはコンパイル時の定数でなければなりません。

10
juanchopanza

C++ 20は(おそらく) std::span を追加します。これにより、次のようなループが可能になります。

#include <iostream>
#include <span>

int main () {
    auto p = new int[5];
    for (auto &v : std::span(p, 5)) {
        v = 1;
    }
    for (auto v : std::span(p, 5)) {
        std::cout << v << '\n';
    }
    delete[] p;
}

残念ながら、これは執筆時点では現在のコンパイラーではまだサポートされていないようです。

もちろん、選択肢がある場合は、get-goのCスタイルの配列よりもstd::vectorを使用することをお勧めします。

1
Baum mit Augen

ポインタのstd::beginに対してstd::endstd::pairを定義する代わりに(ところでstd::でそれらを定義すると ndefined behaviour )、ロールアウトします独自のラッパー 前に推奨 のように、boost::make_iterator_rangeを使用できます。

size_t size = 16;
int *dynamic_array = new int[size];
for (const auto& i : boost::make_iterator_range(dynamic_array, dynamic_array + size))
    std::cout << i << std::endl;

実例

0
Dev Null