2011-01-29 5 views
3

Мне нужен отложенный вызов некоторой функции с аргументами. Есть следующий тестовый код:Связать с одним вызовом копии ctor

#include <functional> 
#include <boost/bind.hpp> 
#include <boost/function.hpp> 

struct test 
{ 
    test() 
    { 
     std::cout << "ctor" << std::endl; 
    } 

    test& operator=(test const& t) 
    { 
     std::cout << "operator=" << std::endl; 
     return *this; 
    } 

    test(test const& t) 
    { 
     std::cout << "copy ctor" << std::endl; 
    } 

    ~test() 
    { 
     std::cout << "dtor" << std::endl; 
    } 
}; 

int foo(test const & t) 
{ 
    return 0; 
} 

int main() 
{ 
    test t; 
    boost::function<int()> f = boost::bind(foo, t); 
    f(); 
    return 0; 
} 

Выход:

ctor 
copy ctor 
copy ctor 
copy ctor 
copy ctor 
dtor 
copy ctor 
dtor 
dtor 
copy ctor 
copy ctor 
copy ctor 
copy ctor 
copy ctor 
copy ctor 
dtor 
dtor 
dtor 
dtor 
dtor 
dtor 
dtor 
dtor 
dtor 

Таким образом, мы можем увидеть, что копия называется т е р в 11 раз !!!

Хорошо. Изменение подталкивание :: привязка к станд :: безвыходном:

int main() 
{ 
    test t; 
    boost::function<int()> f = std::bind(foo, t); 
    f(); 
    return 0; 
} 

Выход:

ctor 
copy ctor 
copy ctor 
copy ctor 
dtor 
copy ctor 
copy ctor 
copy ctor 
copy ctor 
copy ctor 
copy ctor 
dtor 
dtor 
dtor 
dtor 
dtor 
dtor 
dtor 
dtor 
dtor 

Копия т е р называется 9 раз. ОК. Если change boost :: function to std :: function copy ctor будет вызываться только 4 раза. Но это тоже плохое поведение.

Возможно ли это сделать с 1-го вызова копии ctor? std :: ref - плохая идея, потому что он может ссылаться в другой теме и т. д.

Извините за мой плохой английский :) Спасибо.

ответ

1

Используйте выражение лямбда.

int main() 
{ 
    test t; 
    std::function<int()> f = [=](){ foo(t); }; 
    f(); 
    return 0; 
} 

Связывание чрезмерно избыточно из-за невероятной легкости и т. Д. Использования лямбда. Кроме того, вы скомпилировались в режиме Release со всеми оптимизациями, верно?

Вы не получите только один вызов конструктора экземпляра, потому что сначала вы должны создать объект функции, а затем назначить этот объект функции в std :: function. Может быть, это может быть std::move 'd?

Поскольку у вас нет lambdas, и даже привязка производит три копии, вам просто придется вручную написать свой собственный объект функции в этом случае.

+0

Да, режим выпуска и -O2. std :: move возможен в некоторых случаях. – Max

+0

В MinGW с GCC 4.4 У меня есть ошибка: ожидаемое первичное выражение перед '[' токеном на std :: function f = [=]() {foo (t); }; :( – Max

+0

GCC 4.4 не поддерживает lambdas, но вы отметили C++ 0x, поэтому я ожидал, что у вас есть поддержка лямбда. – Puppy

1

Использование lambdas - хороший ответ. Если по какой-либо причине, что не работает для вас, другая возможность сохранения результата связываются в нечто иное, чем станд :: функции:

decltype(std::bind(foo, t)) f = std::bind(foo, t); 

или:

auto f = std::bind(foo, t); 

На моей системе (clang/libC++). Эти выходы:

ctor 
copy ctor 
dtor 
dtor 

Хотя ваше перемещение может отличаться.

+0

Hm, с MinGW (gcc версия 4.4.0 (GCC)) имеет 3 вызова копии ctor :( – Max

0

Это немного зависит от времени жизни объекта, который вы хотите связать.

Предполагая, что срок службы этого объекта включает в себя срок службы функтора, вобще

int main() 
{ 
    test t; 
    boost::function<int()> f(boost::bind(foo, boost::ref(t))); 
    f(); 
    return 0; 
} 

Урожайность один вызов конструктора и деструктора один вызов. :-)

Приветствия & НТН.,

+0

Он уже сказал, что не может использовать ссылку, потому что это многопоточность. – Puppy

+0

@DeadMG: Я не заметил, *, но *, вызываемая функция принимает аргумент ref в 'const', поэтому проблема с защитой от протектора (ну, если он не добавлен путем изменения исходного объекта, а затем просто сделайте * one * копия). –

+0

@Alf: кроме 't' выделяется локально, ссылаясь на него в многопоточной программе на смертный приговор. – Puppy