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 为函数对象提供了一个可重用的适配器模板。它将右值作为常量引用转发,同时保持左值不变。

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 个重载(每个都有和没有 const 限定符),对于每个参数数量(即 2^(Nmax+1) - 2^Nmin)。是的,这意味着编译时的复杂度为 O(2^N),但是系数很低,因此对于合理数量(< 10)的参数效果很好。

描述

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

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

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 以设置最大调用 arity。其默认值为 6。

复杂度

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

描述

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

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

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 以设置最大调用 arity。其默认值为 10。

复杂度

预处理时间:O(N),其中 N 是 arity 限制。编译时间:O(N),其中 N 是调用的有效 arity。运行时:如果编译器内联,则为 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