Boost C++ 库

“...世界上最受推崇和设计精良的C++库项目之一。”
Herb SutterAndrei AlexandrescuC++编码规范

第1章. Boost.Bind

目录

目的
使用 bind 与函数和函数指针
使用 bind 与函数对象
使用 bind 与成员指针
使用嵌套 bind 进行函数组合
重载运算符(Boost 1.33 中新增)
示例
使用 bind 与标准算法
使用 bind 与 Boost.Function
限制
常见问题
为什么这无法编译?
为什么这可以编译?它不应该。
bind(f, ...)bind<R>(f, ...) 之间的区别是什么?
bind 是否适用于 Windows API 函数?
bind 是否适用于 COM 方法?
bind 是否适用于 Mac 工具箱函数?
bind 是否适用于 extern "C" 函数?
为什么 bind 无法自动识别非标准函数?
故障排除
参数数量不正确
无法使用指定的参数调用函数对象
访问不存在的参数
bind(f, ...) 的不当使用
bind<R>(f, ...) 的不当使用
绑定非标准函数
绑定重载函数
建模 STL 函数对象概念
签名中的 const
MSVC 特定:using boost::bind;
MSVC 特定:类模板隐藏函数模板
MSVC 特定:签名中的 ... 被视为类型
接口
概要
通用需求
通用定义
bind
附加重载
实现
文件
依赖项
参数数量
__stdcall__cdecl__fastcallpascal 支持
visit_each 支持
致谢

boost::bind 是标准函数 std::bind1ststd::bind2nd 的泛化。它支持任意函数对象、函数、函数指针和成员函数指针,并且能够将任何参数绑定到特定值或将输入参数路由到任意位置。bind 对函数对象没有任何要求;特别是,它不需要 result_typefirst_argument_typesecond_argument_type 标准类型定义。

给定这些定义

int f(int a, int b)
{
    return a + b;
}

int g(int a, int b, int c)
{
    return a + b + c;
}

bind(f, 1, 2) 将生成一个“零元”函数对象,它不接受任何参数并返回 f(1, 2)。类似地,bind(g, 1, 2, 3)() 等效于 g(1, 2, 3)

可以选择性地只绑定某些参数。 bind(f, _1, 5)(x) 等效于 f(x, 5);这里 _1 是一个 占位符 参数,表示“用第一个输入参数替换”。

为了比较,以下是使用标准库原语表达的相同操作

std::bind2nd(std::ptr_fun(f), 5)(x);

bind 也涵盖了 std::bind1st 的功能

std::bind1st(std::ptr_fun(f), 5)(x);   // f(5, x)
bind(f, 5, _1)(x);                     // f(5, x)

bind 可以处理超过两个参数的函数,其参数替换机制更通用

bind(f, _2, _1)(x, y);                 // f(y, x)
bind(g, _1, 9, _1)(x);                 // g(x, 9, x)
bind(g, _3, _3, _3)(x, y, z);          // g(z, z, z)
bind(g, _1, _1, _1)(x, y, z);          // g(x, x, x)

请注意,在最后一个示例中,由 bind(g, _1, _1, _1) 生成的函数对象不包含对第一个参数以外的任何参数的引用,但它仍然可以与多个参数一起使用。任何额外的参数都会被静默忽略,就像第三个示例中忽略第一个和第二个参数一样。

bind 接受的参数会被复制并由返回的函数对象内部保存。例如,在以下代码中

int i = 5;
bind(f, i, _1);

i 的值的一个副本存储到函数对象中。boost::refboost::cref 可用于使函数对象存储对对象的引用,而不是副本

int i = 5;
bind(f, ref(i), _1);
bind(f, cref(i), _1);

bind 不仅限于函数;它接受任意函数对象。在一般情况下,生成的函数对象的 operator() 的返回类型必须显式指定(如果没有 typeof 运算符,则无法推断返回类型)

struct F
{
    int operator()(int a, int b) { return a - b; }
    bool operator()(long a, long b) { return a == b; }
};

F f;
int x = 104;
bind<int>(f, _1, _1)(x);		// f(x, x), i.e. zero

某些编译器在使用 bind<R>(f, ...) 语法时遇到问题。出于可移植性原因,支持另一种表达上述内容的方法

boost::bind(boost::type<int>(), f, _1, _1)(x);

但是,请注意,备选语法仅作为解决方法提供。它不是接口的一部分。

当函数对象公开名为 result_type 的嵌套类型时,可以省略显式返回类型

int x = 8;
bind(std::less<int>(), _1, 9)(x);	// x < 9

[注意:并非所有编译器都支持省略返回类型。]

默认情况下,bind 会复制提供的函数对象。boost::refboost::cref 可用于使其存储对函数对象的引用,而不是副本。当函数对象不可复制、复制成本高昂或包含状态时,这很有用;当然,在这种情况下,程序员应确保在函数对象仍在使用时不会将其销毁。

struct F2
{
    int s;

    typedef void result_type;
    void operator()(int x) { s += x; }
};

F2 f2 = { 0 };
int a[] = { 1, 2, 3 };

std::for_each(a, a+3, bind(ref(f2), _1));

assert(f2.s == 6);

成员函数指针和数据成员指针不是函数对象,因为它们不支持 operator()。为方便起见,bind 接受成员指针作为其第一个参数,其行为就好像使用了 boost::mem_fn 将成员指针转换为函数对象一样。换句话说,表达式

bind(&X::f, args)

等效于

bind<R>(mem_fn(&X::f), args)

其中 RX::f 的返回类型(对于成员函数)或成员的类型(对于数据成员)。

[注意: mem_fn 创建能够接受指向对象的指针、引用或智能指针作为其第一个参数的函数对象;有关更多信息,请参阅 mem_fn文档]

示例

struct X
{
    bool f(int a);
};

X x;
shared_ptr<X> p(new X);
int i = 5;

bind(&X::f, ref(x), _1)(i);		// x.f(i)
bind(&X::f, &x, _1)(i);			// (&x)->f(i)
bind(&X::f, x, _1)(i);			// (internal copy of x).f(i)
bind(&X::f, p, _1)(i);			// (internal copy of p)->f(i)

最后两个例子很有趣,因为它们生成了“自包含”的函数对象。bind(&X::f, x, _1)存储了x的一个副本。bind(&X::f, p, _1)存储了p的一个副本,并且由于p是一个boost::shared_ptr,函数对象保留了对X实例的引用,即使p超出作用域或被reset(),它仍然有效。

传递给bind的一些参数本身可能是嵌套的bind表达式

bind(f, bind(g, _1))(x);               // f(g(x))

当调用函数对象时,内部bind表达式以未指定的顺序进行求值,先于外部bind;然后,在求值外部bind时,将求值的结果替换到它们的位置。在上面的例子中,当用参数列表(x)调用函数对象时,bind(g, _1)(x)首先被求值,得到g(x),然后bind(f, g(x))(x)被求值,得到最终结果f(g(x))

bind的这个特性可以用来执行函数组合。参见bind_as_compose.cpp,其中有一个例子演示了如何使用bind来实现与Boost.Compose类似的功能。

注意,第一个参数——绑定的函数对象——不会被求值,即使它是由bind生成的函数对象或一个占位符参数,所以下面的例子不会按预期工作。

typedef void (*pf)(int);

std::vector<pf> v;
std::for_each(v.begin(), v.end(), bind(_1, 5));

可以通过一个辅助函数对象apply来实现所需的效果,该对象将其第一个参数(作为函数对象)应用于其参数列表的其余部分。为方便起见,apply的实现已提供在apply.hpp头文件中。以下是修改后的上一例子的样子。

typedef void (*pf)(int);

std::vector<pf> v;
std::for_each(v.begin(), v.end(), bind(apply<void>(), _1, 5));

尽管第一个参数默认情况下不会被求值,但所有其他参数都会被求值。有时需要不求值第一个参数之后的参数,即使它们是嵌套的bind子表达式。这可以使用另一个函数对象protect来实现,该对象会屏蔽类型,以便bind无法识别和求值它。当被调用时,protect只是简单地将参数列表未修改地转发给另一个函数对象。

protect.hpp头文件包含protect的实现。要protect一个bind函数对象免于求值,可以使用protect(bind(f, ...))

为方便起见,由bind生成的函数对象重载了逻辑非运算符!和关系和逻辑运算符==, !=, <, <=, >, >=, &&, ||

!bind(f, ...)等价于bind(logical_not(), bind(f, ...)),其中logical_not是一个函数对象,它接受一个参数x并返回!x

bind(f, ...) op x,其中op是一个关系或逻辑运算符,等价于bind(relation(), bind(f, ...), x),其中relation是一个函数对象,它接受两个参数ab并返回a op b

在实践中,这意味着您可以方便地否定bind的结果。

std::remove_if(first, last, !bind(&X::visible, _1)); // remove invisible objects

并将bind的结果与一个值进行比较。

std::find_if(first, last, bind(&X::name, _1) == "Peter");
std::find_if(first, last, bind(&X::name, _1) == "Peter" || bind(&X::name, _1) == "Paul");

与一个占位符进行比较。

bind(&X::name, _1) == _2

或与另一个bind表达式进行比较。

std::sort(first, last, bind(&X::name, _1) < bind(&X::name, _2)); // sort by name
class image;

class animation
{
public:
    void advance(int ms);
    bool inactive() const;
    void render(image & target) const;
};

std::vector<animation> anims;

template<class C, class P> void erase_if(C & c, P pred)
{
    c.erase(std::remove_if(c.begin(), c.end(), pred), c.end());
}

void update(int ms)
{
    std::for_each(anims.begin(), anims.end(), boost::bind(&animation::advance, _1, ms));
    erase_if(anims, boost::mem_fn(&animation::inactive));
}

void render(image & target)
{
    std::for_each(anims.begin(), anims.end(), boost::bind(&animation::render, _1, boost::ref(target)));
}
class button
{
public:
    boost::function<void()> onClick;
};

class player
{
public:
    void play();
    void stop();
};

button playButton, stopButton;
player thePlayer;

void connect()
{
    playButton.onClick = boost::bind(&player::play, &thePlayer);
    stopButton.onClick = boost::bind(&player::stop, &thePlayer);
}

一般来说,由bind生成的函数对象按引用获取其参数,因此不能接受非常量临时变量或文字常量。这是当前(2003 年)C++语言固有的限制,称为转发问题。(它将在下一个标准(通常称为C++0x)中得到修复。)

库使用以下形式的签名:

template<class T> void f(T & t);

来接受任意类型的参数并将其未修改地传递。如前所述,这对非常量右值不起作用。

在支持函数模板部分排序的编译器上,一个可能的解决方案是添加一个重载:

template<class T> void f(T & t);
template<class T> void f(T const & t);

不幸的是,这需要为九个参数提供 512 个重载,这是不切实际的。库选择了一个小的子集:对于最多两个参数,它完全提供了 const 重载;对于三个或更多个参数,它提供了一个额外的重载,所有参数都通过 const 引用获取。这涵盖了相当一部分用例。

可能是因为您使用了通用的bind<R>(f, ...)语法,从而指示bind不“检查”f 以检测参数个数和返回类型错误。

第一种形式指示bind检查f的类型以确定其参数个数和返回类型。参数个数错误将在“bind 时间”被检测到。当然,此语法对f提出了一些要求。它必须是一个函数、函数指针、成员函数指针或定义名为result_type的嵌套类型的函数对象;简而言之,它必须是bind可以识别的东西。

第二种形式指示bind不要尝试识别f的类型。它通常与不公开或无法公开result_type的函数对象一起使用,但也可以与非标准函数一起使用。例如,当前实现不会自动识别可变参数函数(如printf),因此您必须使用bind<int>(printf, ...)。请注意,出于可移植性原因,支持替代的bind(type<R>(), f, ...)语法。

另一个需要考虑的重要因素是,不支持部分模板特化或函数模板部分排序的编译器在f是函数对象时无法处理第一种形式,并且在大多数情况下,在f是函数(指针)或成员函数指针时无法处理第二种形式。

是的,如果您#define BOOST_BIND_ENABLE_STDCALL。另一种方法是将函数视为泛型函数对象,并使用bind<R>(f, ...)语法。

是的,如果您#define BOOST_BIND_ENABLE_PASCAL。另一种方法是将函数视为泛型函数对象,并使用bind<R>(f, ...)语法。

有时可以。在某些平台上,指向 extern "C" 函数的指针等同于“普通”函数指针,因此它们可以正常工作。其他平台将它们视为不同的类型。bind 的特定于平台的实现应该能够透明地处理这个问题;此实现则不能。像往常一样,解决方法是将函数视为泛型函数对象,并使用bind<R>(f, ...)语法。

一般来说,非可移植扩展应该默认为关闭,以防止厂商锁定。如果相应的宏被自动定义,您可能会在没有意识到代码可能不再可移植的情况下意外地利用它们。此外,一些编译器可以选择将__stdcall (__fastcall)设为默认调用约定,在这种情况下,无需单独的支持。

bind(f, a1, a2, ..., aN)表达式中,函数对象f必须能够恰好接受N个参数。此错误通常在“绑定时”检测到;换句话说,编译错误是在调用bind()的行上报告的。

int f(int, int);

int main()
{
    boost::bind(f, 1);    // error, f takes two arguments
    boost::bind(f, 1, 2); // OK
}

此错误的一个常见变体是忘记成员函数有一个隐式的“this”参数。

struct X
{
    int f(int);
}

int main()
{
    boost::bind(&X::f, 1);     // error, X::f takes two arguments
    boost::bind(&X::f, _1, 1); // OK
}

与正常的函数调用一样,绑定的函数对象必须与参数列表兼容。不兼容通常会在“调用时”由编译器检测到,结果通常是在bind.hpp中的一行出现错误,类似于:

return f(a[a1_], a[a2_]);

此类错误的一个示例:

int f(int);

int main()
{
    boost::bind(f, "incompatible");      // OK so far, no call
    boost::bind(f, "incompatible")();    // error, "incompatible" is not an int
    boost::bind(f, _1);                  // OK
    boost::bind(f, _1)("incompatible");  // error, "incompatible" is not an int
}

占位符_N从“调用时”传递的参数列表中选择位置N处的参数。当然,尝试访问此列表的末尾之后是一个错误。

int f(int);

int main()
{
    boost::bind(f, _1);                  // OK
    boost::bind(f, _1)();                // error, there is no argument number 1
}

错误通常在bind.hpp中报告,类似于以下行:

return f(a[a1_]);

在模拟std::bind1st(f, a)时,此类错误的一个常见错误是键入bind(f, a, _2)而不是正确的bind(f, a, _1)

bind(f, a1, a2, ..., aN)形式会自动识别f的类型。它不适用于任意函数对象;f必须是函数或成员函数指针。

可以使用此形式与定义了result_type的函数对象一起使用,但仅限于支持部分特化和部分排序的编译器。特别是,直到 7.0 版本的 MSVC 都不支持函数对象的此语法。

bind<R>(f, a1, a2, ..., aN)形式支持任意函数对象。

可以使用此形式与函数或成员函数指针一起使用(但不推荐),但仅限于支持部分排序的编译器。特别是,直到 7.0 版本的 MSVC 并不完全支持函数和成员函数指针的此语法。

默认情况下,bind(f, a1, a2, ..., aN)形式识别“普通”C++函数和函数指针。使用不同调用约定的函数或可变参数函数(例如std::printf)不起作用。通用bind<R>(f, a1, a2, ..., aN)形式适用于非标准函数。

在某些平台上,像std::strcmp这样的 extern "C" 函数不被bind的简短形式识别。

另请参见__stdcallpascal支持

尝试绑定重载函数通常会导致错误,因为无法确定要绑定哪个重载。对于具有两个重载(const 和非 const)的成员函数来说,这是一个常见问题,例如在这个简化的示例中:

struct X
{
    int& get();
    int const& get() const;
};

int main()
{
    boost::bind(&X::get, _1);
}

可以通过将(成员)函数指针强制转换为所需类型来手动解决歧义。

int main()
{
    boost::bind(static_cast< int const& (X::*) () const >(&X::get), _1);
}

另一个可以说是更易读的替代方法是引入一个临时变量。

int main()
{
    int const& (X::*get) () const = &X::get;
    boost::bind(get, _1);
}

bind 生成的函数对象并不符合 STL 的 一元函数二元函数 概念,即使这些函数对象是一元或二元操作,因为这些函数对象的类型缺少公共类型定义 result_typeargument_typefirst_argument_typesecond_argument_type。但是,在需要这些类型定义的情况下,可以使用实用程序函数 make_adaptable 将一元和二元函数对象适配到这些概念。这允许将 bind 生成的 一元和二元函数对象与 STL 模板(例如 std::unary_negatestd::binary_negate)结合使用。

make_adaptable 函数定义在 <boost/bind/make_adaptable.hpp> 中,除了 <boost/bind/bind.hpp> 之外,还必须显式包含该文件。

#include <boost/bind/make_adaptable.hpp>

template <class R, class F> unspecified-type make_adaptable(F f);

template<class R, class A1, class F> unspecified-unary-functional-type make_adaptable(F f);

template<class R, class A1, class A2, class F> unspecified-binary-functional-type make_adaptable(F f);

template<class R, class A1, class A2, class A3, class F> unspecified-ternary-functional-type make_adaptable(F f);

template<class R, class A1, class A2, class A3, class A4, class F> unspecified-4-ary-functional-type make_adaptable(F f);

此示例演示如何使用 make_adaptable 创建一个“不是空格”的谓词。

typedef char char_t;
std::locale loc("");
const std::ctype<char_t>& ct = std::use_facet<std::ctype<char_t> >(loc);

auto isntspace = std::not1(boost::make_adaptable<bool, char_t>(boost::bind(&std::ctype<char_t>::is, &ct, std::ctype_base::space, _1)));

在此示例中,bind 创建“是空格”(一元)谓词。然后将其传递给 make_adaptable,以便可以创建一个符合 一元函数 概念的函数对象,作为 std::not1 的参数。

一些编译器,包括 MSVC 6.0 和 Borland C++ 5.5.1,在函数签名中的顶层 const 方面存在问题。

int f(int const);

int main()
{
    boost::bind(f, 1);     // error
}

解决方法:从参数中删除 const 限定符。

在 MSVC(最高到 7.0 版本)中,当使用 using 声明将 boost::bind 引入作用域时

using boost::bind;

语法 bind<R>(f, ...) 将不起作用。解决方法:要么使用限定名称 boost::bind,要么改用 using 指令。

using namespace boost;

在 MSVC(最高到 7.0 版本)中,名为 bind 的嵌套类模板将屏蔽函数模板 boost::bind,从而破坏 bind<R>(f, ...) 语法。不幸的是,一些库包含名为 bind 的嵌套类模板(具有讽刺意味的是,此类代码通常是 MSVC 的特定解决方法)。

解决方法是使用备用 bind(type<R>(), f, ...) 语法。

MSVC(最高到 7.0 版本)将可变参数函数(例如 std::printf)中的省略号视为类型。因此,它将接受(在当前实现中不正确)的形式

bind(printf, "%s\n", _1);

并将拒绝正确的版本

bind<int>(printf, "%s\n", _1);
namespace boost
{
// no arguments

template<class R, class F> unspecified-1 bind(F f);

template<class F> unspecified-1-1 bind(F f);

template<class R> unspecified-2 bind(R (*f) ());

// one argument

template<class R, class F, class A1> unspecified-3 bind(F f, A1 a1);

template<class F, class A1> unspecified-3-1 bind(F f, A1 a1);

template<class R, class B1, class A1> unspecified-4 bind(R (*f) (B1), A1 a1);

template<class R, class T, class A1> unspecified-5 bind(R (T::*f) (), A1 a1);

template<class R, class T, class A1> unspecified-6 bind(R (T::*f) () const, A1 a1);

template<class R, class T, class A1> unspecified-6-1 bind(R T::*f, A1 a1);

// two arguments

template<class R, class F, class A1, class A2> unspecified-7 bind(F f, A1 a1, A2 a2);

template<class F, class A1, class A2> unspecified-7-1 bind(F f, A1 a1, A2 a2);

template<class R, class B1, class B2, class A1, class A2> unspecified-8 bind(R (*f) (B1, B2), A1 a1, A2 a2);

template<class R, class T, class B1, class A1, class A2> unspecified-9 bind(R (T::*f) (B1), A1 a1, A2 a2);

template<class R, class T, class B1, class A1, class A2> unspecified-10 bind(R (T::*f) (B1) const, A1 a1, A2 a2);

// implementation defined number of additional overloads for more arguments
}

namespace
{
 unspecified-placeholder-type-1 _1;

 unspecified-placeholder-type-2 _2;

 unspecified-placeholder-type-3 _3;

// implementation defined number of additional placeholder definitions
}

所有 未指定-N 类型(由 bind 返回)都是 可复制构造 的。未指定-N::result_type 定义为 未指定-N::operator() 的返回类型。

所有 未指定占位符-N 类型都是 可复制构造 的。它们的复制构造函数不会抛出异常。

函数 μ(x, v1, v2, ..., vm)(其中 m 是一个非负整数)定义为:

  • x.get(),当 x 的类型为 boost::reference_wrapper<T>(对于某种类型 T)时;
  • vk,当 x 是(某个)占位符 _k 的副本(对于某个正整数 k)时;
  • x(v1, v2, ..., vm),当 x 是由 bind 返回的函数对象的副本时;
  • 否则为 x
template<class R, class F> unspecified-1 bind(F f)
  • 返回值: 一个函数对象 λ,使得表达式 λ(v1, v2, ..., vm) 等价于 f()(隐式转换为 R)。
  • 抛出: 除非 F 的复制构造函数抛出异常,否则不抛出任何异常。
template<class F> unspecified-1-1 bind(F f)
  • 效果: 等价于 bind<typename F::result_type, F>(f)
  • 注释: 实现允许通过其他方式推断 f 的返回类型作为扩展,而不依赖于 result_type 成员。
template<class R> unspecified-2 bind(R (*f) ())
  • 返回值: 一个函数对象 λ,使得表达式 λ(v1, v2, ..., vm) 等价于 f()
  • 抛出: 不抛出任何异常。
template<class R, class F, class A1> unspecified-3 bind(F f, A1 a1)
  • 返回值: 一个函数对象 λ,使得表达式 λ(v1, v2, ..., vm) 等价于 f(μ(a1, v1, v2, ..., vm))(隐式转换为 R)。
  • 抛出: 除非 FA1 的复制构造函数抛出异常,否则不抛出任何异常。
template<class F, class A1> unspecified-3-1 bind(F f, A1 a1)
  • 效果: 等效于 bind<typename F::result_type, F, A1>(f, a1)
  • 注释: 实现允许通过其他方式推断 f 的返回类型作为扩展,而不依赖于 result_type 成员。
template<class R, class B1, class A1> unspecified-4 bind(R (*f) (B1), A1 a1)
  • 返回值: 一个函数对象 λ,使得表达式 λ(v1, v2, ..., vm) 等效于 f(μ(a1, v1, v2, ..., vm))
  • 抛出异常: 除非 A1 的复制构造函数抛出异常,否则不抛出任何异常。
template<class R, class T, class A1> unspecified-5 bind(R (T::*f) (), A1 a1)
template<class R, class T, class A1> unspecified-6 bind(R (T::*f) () const, A1 a1)
template<class R, class T, class A1> unspecified-6-1 bind(R T::*f, A1 a1)
template<class R, class F, class A1, class A2> unspecified-7 bind(F f, A1 a1, A2 a2)
  • 返回值: 一个函数对象 λ,使得表达式 λ(v1, v2, ..., vm) 等效于 f(μ(a1, v1, v2, ..., vm), μ(a2, v1, v2, ..., vm)),隐式转换为 R
  • 抛出异常: 除非 FA1A2 的复制构造函数抛出异常,否则不抛出任何异常。
template<class F, class A1, class A2> unspecified-7-1 bind(F f, A1 a1, A2 a2)
  • 效果: 等效于 bind<typename F::result_type, F, A1, A2>(f, a1, a2)
  • 注释: 实现允许通过其他方式推断 f 的返回类型作为扩展,而不依赖于 result_type 成员。
template<class R, class B1, class B2, class A1, class A2> unspecified-8 bind(R (*f) (B1, B2), A1 a1, A2 a2)
  • 返回值: 一个函数对象 λ,使得表达式 λ(v1, v2, ..., vm) 等效于 f(μ(a1, v1, v2, ..., vm), μ(a2, v1, v2, ..., vm))
  • 抛出异常: 除非 A1A2 的复制构造函数抛出异常,否则不抛出任何异常。
template<class R, class T, class B1, class A1, class A2> unspecified-9 bind(R (T::*f) (B1), A1 a1, A2 a2)
template<class R, class T, class B1, class A1, class A2> unspecified-10 bind(R (T::*f) (B1) const, A1 a1, A2 a2)

允许实现提供额外的 bind 重载,以支持更多参数或不同的函数指针变体。

此实现支持最多九个参数的函数对象。这是实现细节,而不是设计的固有限制。

某些平台允许几种类型的(成员)函数,这些函数的调用约定不同(函数调用的规则:如何传递参数、如何处理返回值以及谁清理堆栈——如果有的话)。

例如,Windows API 函数和 COM 接口成员函数使用称为 __stdcall 的调用约定。Borland VCL 组件使用 __fastcall。Mac 工具箱函数使用 pascal 调用约定。

要在使用 __stdcall 函数时使用 bind,请在包含 <boost/bind/bind.hpp> 之前定义宏 BOOST_BIND_ENABLE_STDCALL

要在使用 __stdcall 成员函数时使用 bind,请在包含 <boost/bind/bind.hpp> 之前定义宏 BOOST_MEM_FN_ENABLE_STDCALL

要在使用 __fastcall 函数时使用 bind,请在包含 <boost/bind/bind.hpp> 之前定义宏 BOOST_BIND_ENABLE_FASTCALL

要在使用 __fastcall 成员函数时使用 bind,请在包含 <boost/bind/bind.hpp> 之前定义宏 BOOST_MEM_FN_ENABLE_FASTCALL

要将bindpascal函数一起使用,请在包含<boost/bind/bind.hpp>之前#defineBOOST_BIND_ENABLE_PASCAL

要将bind__cdecl成员函数一起使用,请在包含<boost/bind/bind.hpp>之前#defineBOOST_MEM_FN_ENABLE_CDECL

最好在项目选项中定义这些宏,例如在命令行使用-D,或者作为使用bind的翻译单元(.cpp 文件)的第一行。 不遵循此规则可能会导致在头文件包含bind.hpp之前宏未定义时出现难以理解的错误。

[注意:这是一个非可移植的扩展。它不是接口的一部分。]

[注意:一些编译器仅对__stdcall关键字提供有限的支持。]

bind返回的函数对象支持实验性的、目前尚无文档记载的visit_each枚举接口。

参见bind_visitor.cpp 获取示例。

影响库设计的一些早期工作

  • Jaakko Järvi 编写的Binder 库
  • Jaakko Järvi 和 Gary Powell 编写的Lambda 库(现在是 Boost 的一部分);(Binder 库的后续版本);
  • Petter Urkedal 编写的STL 扩展

Doug Gregor 建议使用访问者机制可以让bind与信号/槽库互操作。

John Maddock 修复了bind类型特性库之间特定于 MSVC 的冲突。

在正式评审期间,Ross Smith、Richard Crossley、Jens Maurer、Ed Brey 和其他人提出了许多改进建议。评审经理是 Darin Adler。

在与 Jaakko Järvi 的讨论中,对bind的精确语义进行了改进。

Dave Abrahams 修复了bind迭代器适配器库之间特定于 MSVC 的冲突。

Dave Abrahams 修改了bindmem_fn以在有缺陷的编译器上支持void返回值。

Mac Murrett 贡献了由BOOST_BIND_ENABLE_PASCAL启用的“pascal”支持。

替代的bind(type<R>(), f, ...)语法受到了与 Dave Abrahams 和 Joel de Guzman 讨论的启发。

本文档由 Agustín Bergé 移植到 Quickbook。