Boost C++ 库

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

第 1 章。Boost.Functional/Forward 1.0

Tobias Schwinger

根据 Boost 软件许可协议 1.0 版分发。(请参阅随附文件 LICENSE_1_0.txt 或在 https://boost.ac.cn/LICENSE_1_0.txt 复制)

目录

简要描述
背景
参考
致谢
参考文献

boost::forward_adapter 提供了一个用于函数对象的、可重用的适配器模板。它将右值(RValues)作为常量引用转发,同时保持左值(LValues)不变。

struct g // function object that only accept LValues
{
    template< typename T0, typename T1, typename T2 >
    void operator()(T0 & t0, T1 & t1, T2 & t2) const;

    typedef void result_type;
};

// Adapted version also accepts RValues and forwards
// them as references to const, LValues as-is
typedef boost::forward_adapter<g> f;

另一个适配器 boost::lighweight_forward_adapter 允许转发,但需要用户的一些帮助,即接受并解包引用包装器(参见 Boost.Ref)以处理引用参数,并将所有其他参数限定为常量。

目标函数必须与 Boost.ResultOf 兼容,适配器也是如此。

假设我们有一个函数 f,我们可以像这样调用它

f(123,a_variable);

现在我们想编写另一个通用函数 g,它可以以相同的方式调用,并返回一个对象,该对象使用相同的参数调用 f

f(123,a_variable) == g(f,123,a_variable).call_f()

无论如何,我们为什么要这样做?

也许我们想多次运行 f。或者也许我们想在另一个线程中运行它。也许我们只是想暂时封装调用表达式,然后将其与其他允许组合更复杂表达式的代码一起使用,以便使用 C++ 模板分解它,并让编译器生成一些最终在运行时调用 f 的机制(换句话说;应用一种通常被称为表达式模板的技术)。

现在,我们如何做到这一点?

坏消息是:这是不可能的。

这是因为变量和求值为其值的表达式之间存在细微差别:给定

int y;
int const z = 0;

template< typename T > void func1(T & x);

我们可以调用

func1(y); // x is a reference to a non-const object
func1(z); // x is a reference to a const object

其中

func1(1); // fails to compile.

这样我们可以安全地让 func1 存储其引用参数,并且编译器会阻止我们存储对具有临时生命周期的对象的引用。

重要的是要意识到,非常量性和对象是否绑定到非常量引用参数是两个不同的属性。后者是左值和右值之间的区别。名称源于赋值表达式的左侧和右侧,因此左值通常是可以赋值的,而右值是右侧表达式的临时结果。

y = 1+2;        // a is LValue, 1+2 is the expression producing the RValue,
// 1+2 = a;     // usually makes no sense. 

func1(y);       // works, because y is an LValue
// func1(1+2);  // fails to compile, because we only got an RValue.

如果我们在参数上添加常量限定符,我们的函数也接受右值

template< typename T > void func2(T const & x);

// [...] function scope:
func2(1); // x is a reference to a const temporary, object,
func2(y); // x is a reference to a const object, while y is not const, and
func2(z); // x is a reference to a const object, just like z.

在所有情况下,func2 中的参数 x 都是常量限定的左值。我们可以使用函数重载来识别非常量左值

template< typename T > void func3(T const & x); // #1
template< typename T > void func3(T & x);       // #2

// [...] function scope:
func3(1); // x is a reference to a const, temporary object in #1,
func3(y); // x is a reference to a non-const object in #2, and
func3(z); // x is a reference to a const object in #1.

请注意,重载函数 func3 中的所有参数 x 都是左值。实际上,在 C++98 中,没有办法将右值按原样传输到函数中。另请注意,我们无法区分曾经是常量限定的左值和右值。

这就是我们使用 C++ 98 的方法尽可能接近上面描述的通用转发函数 g 的程度。有关非常详细的讨论,包括需要语言更改的解决方案,请参见 转发问题

现在,为了实际实现它,我们需要针对 N 个参数的 2^N 个重载(每个参数都有和没有常量限定符),针对每个参数数量(即 2^(Nmax+1) - 2^Nmin)。是的,这意味着编译时复杂度为 O(2^N),但是因子很低,因此对于合理数量(< 10)的参数来说效果很好。

描述

函数对象适配器模板,其实例可以使用左值和右值参数调用。右值参数作为引用到常量的类型化左值转发。

可以将元数作为第二个数字非类型模板参数给出,以将转发限制为特定的元数。如果存在第三个数字非类型模板参数,则第二个和第三个模板参数分别被视为最小和最大元数。指定元数有助于提高诊断消息的可读性和编译时间性能。

可以使用 Boost.ResultOf 来确定特定调用表达式的结果类型。

头文件
#include <boost/functional/forward_adapter.hpp>
概要
namespace boost
{
    template< class Function,
        int Arity_Or_MinArity = unspecified, int MaxArity = unspecified >
    class forward_adapter;
}

符号

F

可能是常量限定的函数对象类型或其引用类型

f

可转换为 F 的对象

FA

类型 forward_adapter<F>

fa

使用 f 初始化的 FA 的实例对象

a0...aN

传递给 fa 的参数

目标函数调用的结果类型必须是

boost::result_of<F*(TA0 [const]&...TAN [const]&])>::type

其中 TA0...TAN 表示 a0...aN 的参数类型。

表达式语义

表达式

语义

FA(f)

创建一个适配器,使用 f 初始化目标函数。

FA()

创建一个适配器,尝试使用 F 的默认构造函数。

fa(a0...aN)

使用参数 a0...aN 调用 f

限制

可以定义宏 BOOST_FUNCTIONAL_FORWARD_ADAPTER_MAX_ARITY 来设置最大调用元数。它默认为 6。

复杂度

预处理时间:O(2^N),其中 N 是元数限制。编译时间:O(2^N),其中 N 取决于元数范围。运行时:O(0) 如果编译器内联,否则为 O(1)。

描述

函数对象适配器模板,其实例可以使用左值和右值参数调用。所有参数都作为引用到常量的类型化左值转发,但引用包装器除外,引用包装器会被解包并可能产生非常量左值。

可以将元数作为第二个数字非类型模板参数给出,以将转发限制为特定的元数。如果存在第三个数字非类型模板参数,则第二个和第三个模板参数分别被视为最小和最大元数。指定元数有助于提高诊断消息的可读性和编译时间性能。

可以使用 Boost.ResultOf 来确定特定调用表达式的结果类型。

头文件
#include <boost/functional/lightweight_forward_adapter.hpp>
概要
namespace boost
{
    template< class Function,
        int Arity_Or_MinArity = unspecified, int MaxArity = unspecified >
    struct lightweight_forward_adapter;
}

符号

F

可能是常量限定的函数对象类型或其引用类型

f

可转换为 F 的对象

FA

类型 lightweight_forward_adapter<F>

fa

使用 f 初始化的 FA 的实例

a0...aN

传递给 fa 的参数

目标函数调用的结果类型必须是

boost::result_of<F*(TA0 [const]&...TAN [const]&])>::type

其中 TA0...TAN 表示 a0...aN 的参数类型。

表达式语义

表达式

语义

FA(f)

创建一个适配器,使用 f 初始化目标函数。

FA()

创建一个适配器,尝试使用 F 的默认构造函数。

fa(a0...aN)

使用常量参数 a0...aN 调用 f。如果 aI 是引用包装器,则会将其解包。

限制

可以定义宏 BOOST_FUNCTIONAL_LIGHTWEIGHT_FORWARD_ADAPTER_MAX_ARITY 来设置最大调用元数。它默认为 10。

复杂度

预处理时间:O(N),其中 N 是元数限制。编译时间:O(N),其中 N 是调用的有效元数。运行时:O(0) 如果编译器内联,否则为 O(1)。

由于这些实用程序是从 Boost.Fusion 函数模块中分解出来的,我想感谢 Dan Marsden 和 Joel de Guzman 让我有机会参与开发这个伟大的库。

此外,我要感谢下面参考文献的作者,他们对问题和此处实现的解决方案进行了深入研究。

最后但并非最不重要的一点,我要感谢 Vesa Karnoven 和 Paul Mensonides 的 Boost 预处理器库。如果没有它,我最终将为这个库使用外部代码生成器。

  1. 转发问题, Peter Dimov, Howard E. Hinnant, David Abrahams, 2002
  2. Boost.ResultOf, Douglas Gregor, 2004
  3. Boost.Ref, Jaakko Jarvi, Peter Dimov, Douglas Gregor, David Abrahams, 1999-2002