当我们传递某种类型的实例给一个 bind 表达式时,它将被复制,除非我们显式地告诉 bind 不要复制它。要看我们怎么做,这可能是至关重要的。为了看一下在我们背后发生了什么事情,我们创建一个 tracer 类,它可以告诉我们它什么时候被缺省构造、被复制构造、被赋值,以及被析构。这样,我们就可以很容易看到用不同的方式使用 bind 会如何影响我们传送的实例。以下是完整的 tracer 类。
class tracer {
public:
tracer() {
std::cout << "tracer::tracer()\n";
}
tracer(const tracer& other) {
std::cout << "tracer::tracer(const tracer& other)\n";
}
tracer& operator=(const tracer& other) {
std::cout <<
"tracer& tracer::operator=(const tracer& other)\n";
return *this;
}
~tracer() {
std::cout << "tracer::~tracer()\n";
}
void print(const std::string& s) const {
std::cout << s << '\n';
}
};
我们把我们的 tracer 类用于一个普通的 bind 表达式,象下面这样。
tracer t;
boost::bind(&tracer::print,t,_1)
(std::string("I'm called on a copy of t\n"));
运行这段代码将产生以下输出,可以清楚地看到有很多拷贝产生。
tracer::tracer()
tracer::tracer(const tracer& other)
tracer::tracer(const tracer& other)
tracer::tracer(const tracer& other)
tracer::~tracer()
tracer::tracer(const tracer& other)
tracer::~tracer()
tracer::~tracer()
I'm called on a copy of t
tracer::~tracer()
tracer::~tracer() // 译注:原文没有这一行,有误
如果我们使用的对象的拷贝动作代价昂贵,我们也许就不能这样用 bind 了。但是,拷贝还是有优点的。它意味着 bind 表达式以及由它所得到的绑定器不依赖于原始对象(在这里是 t)的生存期,这通常正是想要的。要避免复制,我们必须告诉 bind 我们想传递引用而不是它所假定的传值。我们要用 boost::ref 和 boost::cref (分别用于引用和 const 引用)来做到这一点,它们也是 Boost.Bind 库的一部分。对我们的 tracer 类使用 boost::ref ,测试代码现在看起来象这样:
tracer t;
boost::bind(&tracer::print,boost::ref(t),_1)(
std::string("I'm called directly on t\n"));
Executing the code gives us this:
tracer::tracer()
I'm called directly on t
tracer::~tracer() // 译注:原文为 tracer::~tracer,有误
这正是我们要的,避免了无谓的复制。bind 表达式使用原始的实例,这意味着没有 tracer 对象的拷贝了。当然,它同时也意味着绑定器现在要依赖于 tracer 实例的生存期了。还有一种避免复制的方法;就是通过指针来传递参数而不是通过值来传递。
tracer t;
boost::bind(&tracer::print,&t,_1)(
std::string("I'm called directly on t\n"));
因此说,bind 总是执行复制。如果你通过值来传递,对象将被复制,这可能对性能有害或者产生不必要的影响。为了避免复制对象,你可以使用 boost::ref/boost::cref 或者使用指针语义。