2011-12-31 3 views
5

は、私はこのような機能を持っていると言う:boost ::関数をコピーするとクロージャもコピーされますか?

void someFunction(const ExpensiveObjectToCopy&); 

私はそれは、その関数はそのクロージャ内のオブジェクトの独自のクローンコピーを保存するかどうか::機能ブーストを行った場合:

boost::function<void()> f = boost::bind(someFunction, x); // <-- f saves a copy of x 

fを渡すようになったら、毎回boost :: functionコピーコンストラクタがそのオブジェクトを再びコピーするか、各関数が同じクロージャを共有しますか? (すなわち、このような)

boost::function<void()> f2 = f; 
callSomeFunction(f); 
etc. 

答えて

4

私はそれがクローン化されたオブジェクトを毎回コピーします(正確には簡単ではない、ソースといくつかの実験の一遍読みから判断)を見つけることができるものから。関数がconst &によって引数をとる場合は不要かもしれませんが、一般にオブジェクトは関数によって変更される可能性があります。オブジェクトのコピーが高価な場合は、参照(boost::refまたはが念頭に置かれます)でキャプチャするのは理にかなっていません。呼び出し元のオブジェクトが存在しない場合は、boost::shared_ptrをキャプチャしてアダプタメソッドはスマートポインタを解凍し、someFunctionを呼び出しますか?

編集:実験では、boost::functionがコピーされるたびにそのオブジェクトをコピーするだけでなく、boost::bindの中に数回もコピーされます。

struct foo_bar { 
    std::vector<int> data; //possibly expensive to copy 
    foo_bar() 
    { std::cout<<"default foo_bar "<<std::endl; } 
    foo_bar(const foo_bar& b):data(b.data) 
    { std::cout<<"copy foo_bar "<<&b<<" to "<<this<<std::endl; } 
    foo_bar& operator=(const foo_bar& b) { 
     this->data = b.data; 
     std::cout<<"asign foo_bar "<<&b<<" to "<<this<<std::endl; 
     return *this; 
    } 
    ~foo_bar(){} 
}; 

void func(const foo_bar& bar) { std::cout<<"func"<<std::endl;} 

int main(int, char*[]) { 
    foo_bar fb; 
    boost::function<void()> f1(boost::bind(func, fb)); 
    std::cout<<"Bind finished"<<std::endl; 
    boost::function<void()> f2(f1); 
    std::cout<<"copy finished"<<std::endl; 
    f1(); 
    f2(); 
    return 0; 
} 

結果出力が続いているように:私は、GCC 4.6と-O2(および-std = C++ 0X)とMinGWの32下ブースト1.45を使用して、次のコードを使用して試験

default foo_bar 
copy foo_bar 0x28ff00 to 0x28ff10 
copy foo_bar 0x28ff10 to 0x28ff28 
copy foo_bar 0x28ff28 to 0x28ff1c 
copy foo_bar 0x28ff1c to 0x28ff34 
copy foo_bar 0x28ff34 to 0x28fed4 
copy foo_bar 0x28fed4 to 0x28fee4 
copy foo_bar 0x28fee4 to 0x28fef4 
copy foo_bar 0x28fef4 to 0x28fe14 
copy foo_bar 0x28fe14 to 0x28fe24 
copy foo_bar 0x28fe24 to 0x28fe34 
copy foo_bar 0x28fe34 to 0x6a2c7c 
Bind finished 
copy foo_bar 0x6a2c7c to 0x6a2c94 
copy finished 
func 
func 

したがって、コピーコンストラクタはf1のバインディングと代入のためにf2を1回と11回作成するために呼び出されました。最初のオブジェクトはスタック上に作成され、コピーのアドレスはそれに非常に近く、わずかに増加しているので、バインディングプロセスは多くの機能を実行します。この場合、コンパイラはインラインではなくオブジェクトを値渡しします。どこにでも結果を保存せずに、単にboost::bindの使用:

int main(int, char*[]) { 
    foo_bar fb; 
    boost::function<void()> f1(boost::bind(func, fb)); 
    return 0; 
} 

default foo_bar 
copy foo_bar 0x28ff00 to 0x28ff10 
copy foo_bar 0x28ff10 to 0x28ff28 
copy foo_bar 0x28ff28 to 0x28ff1c 
copy foo_bar 0x28ff1c to 0x28ff34 
copy foo_bar 0x28ff34 to 0x28fef4 

だから、5つのコピーだけでオブジェクトをバインドします。だから、私は一般的にコードのリモートでパフォーマンスに影響を受けている部分でさえも、値ごとに少なくとも適度なコピーコストを持つものをキャプチャすることを避けるだろう。比較のGCCでstd::tr1::bindstd::bindは、(コードが最初testcodeと基本的に同一である(スタンダード:: TR1 ::機能/ STD ::機能と連動して)はるかに良好に機能するだけで置き換えboost::std::それぞれstd::tr1::有する:

std::tr1::bind with std::tr1::function: 
default foo_bar 
copy foo_bar 0x28ff10 to 0x28ff28 
copy foo_bar 0x28ff28 to 0x28ff34 
copy foo_bar 0x28ff34 to 0x28ff04 
copy foo_bar 0x28ff04 to 0x652c7c 
Bind finished 
copy foo_bar 0x652c7c to 0x652c94 
copy finished 
func 
func 

std::bind with std::function: 
default foo_bar 
copy foo_bar 0x28ff34 to 0x28ff28 
copy foo_bar 0x28ff28 to 0x3c2c7c 
Bind finished 
copy foo_bar 0x3c2c7c to 0x3c2c94 
copy finished 
func 
func 

私はstd::bindが内部呼び出しのためにconst refを渡すか、gccs inlinerにもっとや​​さしく書かれていて、インライン化して冗長コピーコンストラクタを取り除くと書いています。tr1::bindはさらに最適化されてからboost::bindになります。

もちろん、このような種類のテストでは、異なるコンパイルフラグ/コンパイラを使用するYMMV

+0

経由で呼び出さg(const a &)は、私がテストプログラムを書いて、それはあなたが関数オブジェクトをコピーするたびに呼び出されるん格納されたオブジェクトのコピーコンストラクタのように見えたときに呼び出されます。また、boost :: bindはコピーコンストラクタを11回呼び出します。 – Chris

+0

@Chris:Ok。だから、私のテストはよく知っておくべきではありませんでした。だからもし誰かがC++ 11を使うことができるのであればstd :: bindは遠くに行く方法です(私はちょうどlambdasを使うでしょうが)。 – Grizzly

3

値によってオブジェクトをbindに渡すと、コピーされます(テスト済み:11回)。

コピーを作成したくない場合は、boost::crefを使用)を渡してコピーしません。

struct a 
{ 
    a() { std::cout << __func__ << std::endl; } 
    a(const a &) { std::cout << __func__ << std::endl; } 
    ~a() { std::cout << __func__ << std::endl; } 
    const a & operator=(const a & aa) 
    { std::cout << __func__ << std::endl; return aa; } 
}; 

void g(const a &) { std::cout << __func__ << std::endl; } 


void t2() 
{ 
    a aa; 

    boost::function< void() > ff = boost::bind(g, boost::cref(aa)); 
    boost::function< void() > ff2 = ff; 
    std::cout << "after ff" << std::endl; 
    ff(); 
    ff2(); 
    std::cout << "after called ff()" << std::endl; 
} 

出力:ある

a 
after ff 
g 
g 
after called ff() 
~a 

オブジェクトは、関数オブジェクトffを作成したり、それのコピーを作成するときに呼び出さ

  • コンストラクタを作成していないときに呼び出さ

    1. 1つのコンストラクタ(ff2
    2. コンストラクタなしff()またはff2()
    3. と呼ばれるデストラクタオブジェクトがスコープから出てくる
  • 関連する問題