Boost C++ 库

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

第一章 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 的非限定调用。因此,除了任何 boost::get_pointer 重载之外,它还将通过依赖于参数的查找找到在与相应智能指针类相同的命名空间中定义的 get_pointer 重载。]

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

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

不,除非您有充分的理由这样做。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
}

概要中提到的所有未指定-N 类型都是可复制构造的可赋值的。它们的复制构造函数和赋值运算符不会抛出异常。未指定-N::result_type 定义为作为参数传递给 mem_fn 的成员函数指针的返回类型(概要中的 R)。未指定-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.*pmf)(),当 t 是类型 T 或其派生类型的左值时;否则等价于 (get_pointer(t)->*pmf)()
  • 抛出: 无。
template<class R, class T> unspecified-2 mem_fn(R (T::*pmf) () const)
  • 返回值: 一个函数对象 ϝ,使得表达式 ϝ(t) 等价于 (t.*pmf)(),当 t 是类型 T [const] 或其派生类型时;否则等价于 (get_pointer(t)->*pmf)()
  • 抛出: 无。
template<class R, class T> unspecified-2-1 mem_fn(R T::*pm)
  • 返回值: 一个函数对象 ϝ,使得表达式 ϝ(t) 等价于 t.*pm,当 t 是类型 T [const] 或其派生类型时;否则等价于 get_pointer(t)->*pm
  • 抛出: 无。
template<class R, class T, class A1> unspecified-3 mem_fn(R (T::*pmf) (A1))
  • 返回值: 一个函数对象 ϝ,使得表达式 ϝ(t, a1) 等价于 (t.*pmf)(a1),当 t 是类型 T 或其派生类型的左值时;否则等价于 (get_pointer(t)->*pmf)(a1)
  • 抛出: 无。
template<class R, class T, class A1> unspecified-4 mem_fn(R (T::*pmf) (A1) const)
  • 返回值: 一个函数对象 ϝ,使得表达式 ϝ(t, a1) 等价于 (t.*pmf)(a1),当 t 是类型 T [const] 或其派生类型时;否则等价于 (get_pointer(t)->*pmf)(a1)
  • 抛出: 无。
template<class R, class T, class A1, class A2> unspecified-5 mem_fn(R (T::*pmf) (A1, A2))
  • 返回值: 一个函数对象 ϝ,使得表达式 ϝ(t, a1, a2) 等价于 (t.*pmf)(a1, a2),当 t 是类型 T 或其派生类型的左值时;否则等价于 (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, a1, a2) 等价于 (t.*pmf)(a1, a2),当 t 是类型 T [const] 或其派生类型时;否则等价于 (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。