次の構文の範囲ベースの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 *&)'の呼び出しに一致する関数がありません
動的配列でこの新しい構文を使用するにはどうすればよいですか?
範囲ベースの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;
_
コンパイラはこの配列の開始と終了を推定できないため、動的に割り当てられた配列でrange-for-loopを使用することはできません。代わりに、常にコンテナを使用する必要があります。例 std::vector
。
std::vector<int> v(size);
for(const auto& elem: v)
// do something
最初の要素へのポインタしかないため、動的に割り当てられた配列に対して範囲ベースのループを直接実行することはできません。コンパイラがループを実行するために使用できるサイズに関する情報はありません。慣用的な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;
ただし、どちらの場合も、サイズはコンパイル時の定数でなければなりません。
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
を使用することをお勧めします。
ポインタのstd::begin
に対してstd::end
とstd::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;
実例 。