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 列表,以便我们保持此表是最新的。

基本用法

函数包装器只需通过使用所需的返回类型和参数类型(以 C++ 函数类型形式表示)来实例化 function 类模板即可定义。可以提供任意数量的参数,直到某个实现定义的限制(默认为 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 要求编译器能 reasonably conform to the C++ standard。(reasonably conform to the C++ standard)

函数对象引用

在某些情况下,复制 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