本の虫

著者:江添亮
ブログ: http://cpplover.blogspot.jp/
メール: boostcpp@gmail.com
Twitter: https://twitter.com/EzoeRyou
GitHub: https://github.com/EzoeRyou

アマゾンの江添のほしい物リストを著者に送るとブログ記事のネタになる

筆者にブログのネタになる品物を直接送りたい場合、住所をメールで質問してください。

やねうらおが本当に必要だったもの

range-based forはコンピューター将棋で使えるのか? | やねうら王 公式サイト

やねうらおが、range-based forが使えないとこぼしている。しかしその利用例をみるに、そもそもrange-based forを使うべきではない。

range-based forは、イテレーターというコンセプト(まだコンセプト機能はC++にないが)に従っている。イテレーターはポインター操作をモデルとしている。

一方、やねうらおのコードは、本来イテレーターではないものを無理やりイテレーターのようにしている。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