range-based forはコンピューター将棋で使えるのか? | やねうら王 公式サイト
やねうらおが、range-based forが使えないとこぼしている。しかしその利用例をみるに、そもそもrange-based forを使うべきではない。
range-based forは、イテレーターというコンセプト(まだコンセプト機能はC++にないが)に従っている。イテレーターはポインター操作をモデルとしている。
- イテレーターは要素群の中のある要素を指していて、operator *で要素を参照できる。
- イテレーターの指す要素は、operator ++で次の要素に移動できる。
- イテレーターはhalf-openとなっていて、要素群の最後の要素のひとつ次の、何も指していない状態のイテレーターが存在する。これを番兵のように使い要素の端に到達したことを判定する
一方、やねうらおのコードは、本来イテレーターではないものを無理やりイテレーターのようにしている。Squareというのは、おそらくunscoped enumだ。(C++11ではscoped enumという強い型をもつenumが追加された。従来の弱いenumはunscoped enumと呼ばれる)。つまり整数型と番兵なのだろう。
したがって、やねうらおに必要なのはrange-based forではない。
ところで、記事中でやねうらおはCプリプロセッサーマクロを多用している。これは極めて醜悪だ。C++にはlambda式が存在するので、同等のコードはもっと綺麗に書ける。
おそらく、やねうらおが本当にほしかったのは、こういうものではないのか。
template < typename T, typename Func >
void yaneu_for( T i, T sentinel, Func func )
{
for ( ; i != sentinel ; ++i )
{
func( i ) ;
}
}
int main()
{
yaneu_for( ZERO, NB,
[]( auto i )
{
// 処理
} ) ;
int hoge ;
yaneu_for( ZERO, NB,
[&]( auto i )
{
// lambda式の外の変数も使える
hoge = i ;
} ) ;
}
初期値iと番兵sentinelを指定すると、sentinelに到達するまでインクリメントしつつ関数オブジェクトfuncを呼ぶ。関数オブジェクトはlambda式を使うとその場に書ける。
ところで、range-based for文は、以下のように使う。
for ( for-range-declaration : range )
statement
これは、以下のように展開される(多少の細部を省略している)
{ // 新しいブロックスコープ
auto && __range = range ;
for ( auto __begin = begin-expr,
__end = end-expr ;
__begin != end ;
++__begin ) {
for-range-declaration = *__begin() ;
statement
}
}
begin-exprとend-exprは、decltype(__range)の型次第で変わるが、いずれもbegin()/end()に相当する処理である。
具体的な例をしめす。以下のようなコードは、
std::vector<int> v{ 1, 2, 3 } ;
for ( auto i : v )
{
std::cout << i << std::endl ;
}
以下のように展開される。
std::vector<int> v{ 1, 2, 3 } ;
{
auto && __range = v ;
for ( auto __begin = v.begin(), __end = v.end() ;
__begin != __end ;
++__begin )
{
auto i = *__begin ;
std::cout << i << std::endl ;
}
}
v.end()は一度しか呼ばれない。
以下のような場合、
int a[100] ;
for ( int & i : a )
{
i = 0 ;
}
以下のように展開される。
int a[100] ;
{
auto && __range = a ;
for ( __begin = a, __end = a + 100 ;
__begin != end ;
++__begin ; )
{
int & i = *__begin ;
i = 0 ;
}
}
それから、もはや現代でinlineを使う理由が思いつかない。
ドワンゴ広告
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0
レンジベースドフォー便利ですよね。
ReplyDelete便利すぎてもうちょっと拡張できないかよく考えます。
個人的には、
for(auto& o:10){/**/}
って書いたら0->10で展開してほしいのですけど、
なんかすげー面倒なクラス書かないと実現しないのですよね。
もしくはコンテナ初期化して使うか。それも無駄が多いですし。
標準でなんらかのカバークラス出してくれないか待ってマース。
やねうって完全にオワコンだしクソみたいなコードしか書けないからしゃーない
ReplyDeletettp://ideone.com/C6TCiG
ReplyDeleteこんな感じのコードが標準にマージされてほしい人生でした。
逐一、過去のコード漁ってポートしてくるの面倒なんで標準に入れてくれたらなーと、淡い期待をしています。