2010-11-29

std::functionのtargetの使い方

こういうことができる。

void f() { std::cout << "f" << std::endl ; }
void g() { std::cout << "g" << std::endl ; }

int main()
{
    std::function< void ( void ) > func( &f ) ;
    func() ; // f

    *func.target< void (*)(void) >() = &g ;
    func() ; // g
}

誰が使うんだろう。

追記:これだけだと、誤解を招きそうなので、以下のコードを補足しておく。

void f() { std::cout << "f" << std::endl ; }
void g() { std::cout << "g" << std::endl ; }
struct h { void operator ()(void){} ; } ;

int main()
{
    std::function< void ( void ) > func( &f ) ;
    func() ; // f

    *func.target< void (*)(void) >() = &g ;
    func() ; // g

    func = h() ;
    // 実行時エラー、型が合わない
    *func.target< void (*)(void) >() = &g ;
}

一応、std::function経由で呼び出すには、格納されている型を実行時に判断しなければならない。そのため、すでに格納されている型が分かっているのならば、中身を取り出すことで、そのような実行時チェックを、中身を取り出す際の一回で済ませることができる。これは、変更をしないと保証できるstd::functionのオブイェクトに対し、何百万回もoperator ()を呼び出す際に、有効であろう。そんなケースはあまりないと思うが。

void f( std::function < int (int) > f )
{
    // 1000000回の実行時チェックが必要
    for ( int i = 0 ; i != 1000000 ; ++i )
    {
        f(i) ;
    }
}

void g( std::function < int (int) > f )
{
    // fには関数ポインターが入っていると分かっている
    auto func = *f.target< int (*)(int) >() ;
    // 実行時チェックはいらない
    for ( int i = 0 ; i != 1000000 ; ++i )
    {
        func(i) ;
    }
}

もちろん、関数gを以下のように呼び出した場合、実行時エラーになる。

struct Foo { int operator ()(int) const { return 0 ; } } ;

int main()
{
    f( Foo() ) ; // OK
    g( Foo() )  ; // エラー
}

targetは危険である。規格を読んでstd::functionを実装した者でない限り、targetを使ってはいけない。つまり、私は使う資格がある。使いたいとは思わないが。

なぜならば、gは仮引数fの中身の型が、関数ポインターであることを前提にしているからである。

No comments:

Post a Comment

You can use some HTML elements, such as <b>, <i>, <a>, also, some characters need to be entity referenced such as <, > and & Your comment may need to be confirmed by blog author. Your comment will be published under GFDL 1.3 or later license with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.