Boost C++ 库

...是世界上最受推崇、设计最精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ Coding Standards

教程 - Boost C++ 函数库
PrevUpHomeNext

教程

Boost.Function 有两种语法形式:首选形式和兼容形式。首选形式更贴近 C++ 语言,并减少了需要考虑的独立模板参数数量,通常能提高可读性;然而,由于编译器错误,首选形式并非在所有平台上都受支持。兼容形式可在 Boost.Function 支持的所有编译器上正常工作。请参考下表来确定为您的编译器选择哪种语法形式。

首选语法 兼容语法
  • GNU C++ 2.95.x, 3.0.x 及更高版本
  • Comeau C++ 4.2.45.2
  • SGI MIPSpro 7.3.0
  • Intel C++ 5.0, 6.0
  • Compaq's cxx 6.2
  • Microsoft Visual C++ 7.1 及更高版本
  • 任何支持首选语法的编译器
  • Microsoft Visual C++ 6.0, 7.0
  • Borland C++ 5.5.1
  • Sun WorkShop 6 update 2 C++ 5.3
  • Metrowerks CodeWarrior 8.1

如果您的编译器未在此列表中出现,请尝试使用首选语法,并将您的结果报告给 Boost 列表,以便我们保持此表最新。

基本用法

函数包装器通过使用期望的返回类型和参数类型实例化 function 类模板来定义,这些类型以 C++ 函数类型的形式给出。可以提供任意数量的参数,最多可达某个由实现定义的限制(默认为 10)。以下代码声明了一个函数对象包装器 f,它接受两个 int 参数并返回一个 float

首选语法 兼容语法
boost::function<float (int x, int y)> f;
boost::function2<float, int, int> f;

默认情况下,函数对象包装器是空的,因此我们可以创建一个函数对象来赋给 f

struct int_div { 
  float operator()(int x, int y) const { return ((float)x)/y; }; 
};

f = int_div();

现在我们可以使用 f 来执行底层函数对象 int_div

std::cout << f(5, 3) << std::endl;

我们可以将任何兼容的函数对象赋给 f。如果 int_div 被声明为接受两个 long 操作数,隐式转换将在没有任何用户干预的情况下应用于参数。参数类型的唯一限制是它们必须是 CopyConstructible 的,所以我们甚至可以使用引用和数组。

首选语法
boost::function<void (int values[], int n, int& sum, float& avg)> sum_avg;

兼容语法
boost::function4<void, int*, int, int&, float&> sum_avg;

void do_sum_avg(int values[], int n, int& sum, float& avg)
{
  sum = 0;
  for (int i = 0; i < n; i++)
    sum += values[i];
  avg = (float)sum / n;
}

sum_avg = &do_sum_avg;

调用一个实际上不包含函数对象的函数对象包装器是一种前置条件违反,类似于尝试通过空函数指针进行调用,并且会抛出 bad_function_call 异常)。我们可以通过在布尔上下文中(如果包装器非空则求值为 true)使用它,或者将其与 0 进行比较来检查一个空的函数对象包装器。例如:

if (f)
  std::cout << f(5, 3) << std::endl;
else
  std::cout << "f has no target, so it is unsafe to call" << std::endl;

或者,empty() 方法将返回包装器是否为空。

最后,我们可以通过将其赋给 0 或调用 clear() 成员函数来清除函数目标,例如:

f = 0;

自由函数

自由函数指针可以被视为具有 const 函数调用运算符的单例函数对象,因此可以直接与函数对象包装器一起使用。

float mul_ints(int x, int y) { return ((float)x) * y; }

f = &mul_ints;

请注意,除非您碰巧使用的是 Microsoft Visual C++ 6 版本,否则 & 并非真正必需。

成员函数

在许多系统中,回调函数经常调用特定对象的成员函数。这通常被称为“参数绑定”,超出了 Boost.Function 的范围。然而,直接使用成员函数是支持的,因此以下代码是有效的:

struct X {
  int foo(int);
};

首选语法 兼容语法
boost::function<int (X*, int)> f;

f = &X::foo;
  
X x;
f(&x, 5);
boost::function2<int, X*, int> f;

f = &X::foo;
  
X x;
f(&x, 5);

存在多个库支持参数绑定。下面总结了三个这样的库:

  • Bind。该库允许绑定任何函数对象的参数。它轻量级且高度可移植。

  • C++ 标准库。结合使用 std::bind1ststd::mem_fun,可以绑定指向成员函数的指针对象以供 Boost.Function 使用。

    首选语法 兼容语法
      boost::function<int (int)> f;
    X x;
    f = std::bind1st(
          std::mem_fun(&X::foo), &x);
    f(5); // Call x.foo(5)
      boost::function1<int, int> f;
    X x;
    f = std::bind1st(
          std::mem_fun(&X::foo), &x);
    f(5); // Call x.foo(5)

  • Lambda 库。该库提供了一个强大的组合机制来构建使用非常自然的 C++ 语法的函数对象。Lambda 需要一个相对符合 C++ 标准的编译器。

函数对象的引用

在某些情况下,让 Boost.Function 克隆函数对象会很昂贵(或在语义上不正确)。在这些情况下,可以要求 Boost.Function 只保留对实际函数对象的引用。这是通过使用 refcref 函数来包装函数对象的引用来实现的。

首选语法 兼容语法
stateful_type a_function_object;
boost::function<int (int)> f;
f = boost::ref(a_function_object);

boost::function<int (int)> f2(f);
stateful_type a_function_object;
boost::function1<int, int> f;
f = boost::ref(a_function_object);

boost::function1<int, int> f2(f);

在此,f 不会复制 a_function_object,当 f2 被指向 fa_function_object 的引用时也不会。此外,在使用函数对象的引用时,Boost.Function 不会抛出赋值或构造异常。

比较 Boost.Function 函数对象

可以通过 ==!= 与任何可以存储在包装器中的函数对象来比较函数对象包装器。如果函数对象包装器包含该类型的函数对象,则会将其与给定的函数对象进行比较(该对象必须是 EqualityComparable 或具有重载的 boost::function_equal)。例如:

int compute_with_X(X*, int);

f = &X::foo;
assert(f == &X::foo);
assert(&compute_with_X != f);

当与 reference_wrapper 的实例进行比较时,reference_wrapper 中对象的地址将与函数对象包装器存储的对象的地址进行比较。

a_stateful_object so1, so2;
f = boost::ref(so1);
assert(f == boost::ref(so1));
assert(f == so1); // Only if a_stateful_object is EqualityComparable
assert(f != boost::ref(so2));

PrevUpHomeNext