2011-12-31 8 views
5

내가이 같은 기능을 가지고 말 :boost :: function을 복사해도 클로저가 복사됩니까?

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::cref을 염두에 두어야 함)로 캡처하거나 원래 개체가 호출 지점에 존재하지 않으면 boost::shared_ptr을 캡처하고 어댑터를 작성하십시오 메서드는 smartpointer를 언팩하고 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; 
} 

결과 출력은 다음에 같이 : I는 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를 한 번 및 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 부를 그냥 객체를 바인딩합니다. 따라서 코드의 원격 성능에 민감한 부분이라 할지라도 가치 당 최소한의 복사 비용을 가진 것은 캡처하지 않는 것이 좋습니다. 비교 gccs에서 std::tr1::bindstd::bind는 (코드가 제 testcode와 기본적으로 동일하다 (표준 : TR1 :: 기능/표준 : 기능과 관련하여) 더 나은 수행 단지 대체 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를 전달하거나, gcc inliner에 더 친숙한 방식으로 작성되어 중복 된 복사본 생성자를 인라인하고 제거합니다. tr1::bind은 여전히 ​​최적 상태가 아니지만 여전히 boost::bind으로 최적화되어 있습니다.

물론 이런 종류의 테스트를 통해 항상 다른 컴파일 플래그/컴파일러를 사용하는 YMMV

+0

나오는 때 호출 내가 테스트 프로그램을 작성하고 당신이 함수 개체를 복사 할 때마다 호출되는 않는 저장된 객체의 복사 생성자처럼 보인다 . 또한 boost :: bind는 복사 생성자를 11 번 호출합니다! – Chris

+0

@Chris : 알겠습니다. 그래서 제 테스트는 우연히 알게 된 것이 아닙니다. 그래서 사람이 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. 한 생성자 (ff2)
    2. 생성자 없음 g(const a &)라는 ff() 또는 ff2()
    3. 소멸자를 통해 호출 할 때 개체가 범위