for-range ループ構文で、生のポインターを範囲のように動作させるにはどうすればよいですか。
double five = 5;
double* dptr = &five;
for(int& d : dptr) std::cout << d << std::endl;// will not execute if the pointer is null
動機:
boost::optional
(将来のstd::optional
) 値を範囲と見なすことができるため、 for range ループ http://faithandbrave.hateblo.jp/entry/2015/01/29/173613 で使用できるようになりました。
私が自分の簡易版を書き直したとき:
namespace boost {
template <class Optional>
decltype(auto) begin(Optional& opt) noexcept{
return opt?&*opt:nullptr;
}
template <class Optional>
decltype(auto) end(Optional& opt) noexcept{
return opt?std::next(&*opt):nullptr;
}
}
使用されます
boost::optional<int> opt = 3;
for (int& x : opt) std::cout << x << std::endl;
そのコードを見ながら、生の (null 許容の) ポインターにも一般化できると想像しました。
double five = 5;
double* dptr = &five;
for(int& d : dptr) std::cout << d << std::endl;
通常の代わりにif(dptr) std::cout << *dptr << std::endl;
。これは問題ありませんが、上記の他の構文を実現したかったのです。
試み
Optional
最初に、上記のバージョンのポインターを作成して動作させようとしましたが、できませんでしbegin
た。end
そのため、型を明示し、すべてのテンプレートを削除することにしました。
namespace std{ // excuse me, this for experimenting only, the namespace can be removed but the effect is the same.
double* begin(double* opt){
return opt?&*opt:nullptr;
}
double* end(double* opt){
return opt?std::next(&*opt):nullptr;
}
}
ほとんどそこに、それはのために働く
for(double* ptr = std::begin(dptr); ptr != std::end(dptr); ++ptr)
std::cout << *ptr << std::endl;
しかし、おそらく同等の for-range ループでは機能しません。
for(double& d : dptr) std::cout << d << std::endl;
2 つのコンパイラが教えてくれます。error: invalid range expression of type 'double *'; no viable 'begin' function available
何が起こっている?範囲ループがポインターに対して機能することを禁止するコンパイラーの魔法はありますか。範囲ループ構文について間違った仮定をしていますか?
皮肉なことに、標準にはオーバーロードがstd::begin(T(&arr)[N])
あり、これはそれに非常に近いものです。
注意してください。
はい、その考えはばかげています。
double* ptr = new double[10];
for(double& d : ptr){...}
最初の要素のみを反復します。より明確で現実的な回避策は、@Yakk によって提案された回避策のようなことを行うことです。
for(double& d : boost::make_optional_ref(ptr)){...}
このようにして、1 つの要素のみを繰り返し処理していること、およびその要素がオプションであることは明らかです。
わかりました、わかりました、に戻りif(ptr) ... use *ptr
ます。