Boost C++ 库

...世界上最受推崇和专业设计的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码标准

第 1 章。 Boost.Member Function

目录

目的
常见问题解答
是否可以使用 mem_fn 代替标准 std::mem_fun[_ref] 适配器?
我是否应该在现有代码中将每个 std::mem_fun[_ref] 出现的地方都替换为 mem_fn
mem_fn 是否适用于 COM 方法?
为什么 BOOST_MEM_FN_ENABLE_STDCALL 没有自动定义?
接口
概要
通用要求
get_pointer
mem_fn
实现
文件
依赖项
参数数量
__stdcall__cdecl__fastcall 支持
致谢

boost::mem_fn 是标准函数 std::mem_funstd::mem_fun_ref 的泛化。它支持具有多个参数的成员函数指针,并且返回的函数对象可以将指向对象实例的指针、引用或智能指针作为其第一个参数。mem_fn 还支持指向数据成员的指针,方法是将它们视为不带参数并返回对成员的(const)引用的函数。

mem_fn 的目的是双重的。首先,它允许用户在容器上调用成员函数,即使容器存储了智能指针,也可以使用熟悉的

std::for_each(v.begin(), v.end(), boost::mem_fn(&Shape::draw));

语法。

其次,库开发人员可以使用它作为构建块,他们希望将成员函数指针视为函数对象。库可能会定义一个增强的 for_each 算法,其重载形式为

template<class It, class R, class T> void for_each(It first, It last, R (T::*pmf) ())
{
    std::for_each(first, last, boost::mem_fn(pmf));
}

这将允许方便的语法

for_each(v.begin(), v.end(), &Shape::draw);

在记录该功能时,库作者只需声明

template<class It, class R, class T> void for_each(It first, It last, R (T::*pmf) ());
  • 效果: 等效于 std::for_each(first, last, boost::mem_fn(pmf))

其中 boost::mem_fn 可以是指向此页面的链接。有关示例,请参阅 bind 的文档

mem_fn 接受一个参数,即指向成员的指针,并返回一个适用于标准或用户定义算法的函数对象

struct X
{
    void f();
};

void g(std::vector<X> & v)
{
    std::for_each(v.begin(), v.end(), boost::mem_fn(&X::f));
};

void h(std::vector<X *> const & v)
{
    std::for_each(v.begin(), v.end(), boost::mem_fn(&X::f));
};

void k(std::vector<boost::shared_ptr<X> > const & v)
{
    std::for_each(v.begin(), v.end(), boost::mem_fn(&X::f));
};

返回的函数对象接受与输入成员函数相同的参数,外加一个“灵活”的第一个参数,该参数表示对象实例。

当使用既不是指针也不是对适当类(上面示例中的 X)的引用的第一个参数 x 调用函数对象时,它使用 get_pointer(x)x 获取指针。库作者可以通过提供适当的 get_pointer 重载来“注册”他们的智能指针类,从而允许 mem_fn 识别和支持它们。

[注意: get_pointer 不限于返回指针。任何可以在成员函数调用表达式 (x->*pmf)(...) 中使用的对象都可以工作。]

[注意: 该库使用对 get_pointer 的非限定调用。因此,它将通过参数依赖查找找到与相应的智能指针类在同一命名空间中定义的 get_pointer 重载,以及任何 boost::get_pointer 重载。]

mem_fn 返回的所有函数对象都公开一个 result_type typedef,它表示成员函数的返回类型。对于数据成员,result_type 定义为成员的类型。

是的。对于简单的用途,mem_fn 提供了标准适配器不具备的附加功能。使用 std::bind1ststd::bind2ndBoost.Compose 以及标准适配器的复杂表达式可以使用自动利用 mem_fnboost::bind 重写。

不,除非您有充分的理由这样做。mem_fn 与标准适配器并非 100% 兼容,尽管它非常接近。特别是,mem_fn 不返回 std::[const_]mem_fun[1][_ref]_t 类型的对象,因为标准适配器会这样做,并且不可能使用标准 argument_typefirst_argument_type 嵌套 typedef 完全描述第一个参数的类型。需要可适应函数对象才能正常运行的库可能不喜欢 mem_fn

通常,非可移植扩展应默认为关闭,以防止供应商锁定。如果 BOOST_MEM_FN_ENABLE_STDCALL 已自动定义,则您可能在没有意识到您的代码可能不再可移植的情况下意外地利用了它。此外,默认调用约定可能是 __stdcall,在这种情况下,启用 __stdcall 支持将导致重复定义。

namespace boost
{
    template<class T> T * get_pointer(T * p);

    template<class R, class T> unspecified-1 mem_fn(R (T::*pmf) ());

    template<class R, class T> unspecified-2 mem_fn(R (T::*pmf) () const);

    template<class R, class T> unspecified-2-1 mem_fn(R T::*pm);

    template<class R, class T, class A1> unspecified-3 mem_fn(R (T::*pmf) (A1));

    template<class R, class T, class A1> unspecified-4 mem_fn(R (T::*pmf) (A1) const);

    template<class R, class T, class A1, class A2> unspecified-5 mem_fn(R (T::*pmf) (A1, A2));

    template<class R, class T, class A1, class A2> unspecified-6 mem_fn(R (T::*pmf) (A1, A2) const);

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

概要中提到的所有 unspecified-N 类型都是 CopyConstructibleAssignable。它们的复制构造函数和赋值运算符不会引发异常。unspecified-N::result_type 定义为作为参数传递给 mem_fn 的成员函数指针的返回类型(概要中的 R)。unspecified-2-1::result_type 定义为 R

template<class T> T * get_pointer(T * p)
  • 返回值: p
  • 抛出: 无。
template<class R, class T> unspecified-1 mem_fn(R (T::*pmf) ())
  • 返回值: 函数对象 ϝ,使得当 t 是类型 T 或派生的左值时,表达式 ϝ(t) 等效于 (t.*pmf)();否则等效于 (get_pointer(t)->*pmf)()
  • 抛出: 无。
template<class R, class T> unspecified-2 mem_fn(R (T::*pmf) () const)
  • 返回值: 函数对象 ϝ,使得当 t 是类型 T [const] 或派生类型时,表达式 ϝ(t) 等效于 (t.*pmf)();否则等效于 (get_pointer(t)->*pmf)()
  • 抛出: 无。
template<class R, class T> unspecified-2-1 mem_fn(R T::*pm)
  • 返回值: 函数对象 ϝ,使得当 t 是类型 T [const] 或派生类型时,表达式 ϝ(t) 等效于 t.*pm;否则等效于 get_pointer(t)->*pm
  • 抛出: 无。
template<class R, class T, class A1> unspecified-3 mem_fn(R (T::*pmf) (A1))
  • 返回值: 函数对象 ϝ,使得当 t 是类型 T 或派生的左值时,表达式 ϝ(t, a1) 等效于 (t.*pmf)(a1);否则等效于 (get_pointer(t)->*pmf)(a1)
  • 抛出: 无。
template<class R, class T, class A1> unspecified-4 mem_fn(R (T::*pmf) (A1) const)
  • 返回值: 函数对象 ϝ,使得当 t 是类型 T [const] 或派生类型时,表达式 ϝ(t, a1) 等效于 (t.*pmf)(a1);否则等效于 (get_pointer(t)->*pmf)(a1)
  • 抛出: 无。
template<class R, class T, class A1, class A2> unspecified-5 mem_fn(R (T::*pmf) (A1, A2))
  • 返回值: 函数对象 ϝ,使得当 t 是类型 T 或派生的左值时,表达式 ϝ(t, a1, a2) 等效于 (t.*pmf)(a1, a2);否则等效于 (get_pointer(t)->*pmf)(a1, a2)
  • 抛出: 无。
template<class R, class T, class A1, class A2> unspecified-6 mem_fn(R (T::*pmf) (A1, A2) const)
  • 返回值: 函数对象 ϝ,使得当 t 是类型 T [const] 或派生类型时,表达式 ϝ(t, a1, a2) 等效于 (t.*pmf)(a1, a2);否则等效于 (get_pointer(t)->*pmf)(a1, a2)
  • 抛出: 无。

此实现最多支持具有八个参数的成员函数。这不是设计的固有限制,而是实现细节。

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

例如,Windows API 函数和 COM 接口成员函数使用称为 __stdcall 的调用约定。Borland VCL 组件使用 __fastcall。UDK,OpenOffice.org 的组件模型,使用 __cdecl

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

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

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

最好在项目选项中定义这些宏,通过命令行上的 -D,或作为使用 mem_fn 的转换单元 (.cpp 文件) 中的第一行。 不遵循此规则可能会在头文件在宏定义之前包含 mem_fn.hpp 时导致模糊的错误。

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

[注意: 某些编译器仅提供对 __stdcall 关键字的最低限度支持。]

  • Rene Jager 最初关于使用 traits 类使 mem_fn 适应用户定义的智能指针的建议启发了基于 get_pointer 的设计。
  • 在正式审查期间,Richard Crossley、Jens Maurer、Ed Brey 和其他人提出了许多改进建议。审查经理是 Darin Adler。
  • Steve Anichini 指出 COM 接口使用 __stdcall
  • Dave Abrahams 修改了 bindmem_fn 以支持缺陷编译器上的 void 返回值。
  • Daniel Boelzle 指出 UDK 使用 __cdecl

Agustín Bergé 将此文档移植到 Quickbook。