版权所有 © 2007, 2008 Tobias Schwinger
根据 Boost 软件许可证版本 1.0 发布。(参见随附文件 LICENSE_1_0.txt 或在 https://boost.ac.cn/LICENSE_1_0.txt 复制)
boost::forward_adapter
为函数对象提供了一个可重用的适配器模板。它将右值(RValues)转发为 const 引用,而左值(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)作为引用参数,并对所有其他参数进行 const 限定。
目标函数必须与 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;
and
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
存储其引用参数,并且编译器阻止我们存储对具有临时生命周期的对象的引用。
重要的是要认识到,非 const 性以及对象是否绑定到非 const 引用参数是两个不同的属性。后者是左值(LValues)和右值(RValues)之间的区别。这些名称源自赋值表达式的左侧和右侧,因此左值通常是你可以赋值的对象,而右值是右侧表达式的临时结果。
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.
如果我们对参数添加 const 限定,我们的函数也将接受右值:
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
都是 const 限定的左值。我们可以使用函数重载来识别非 const 左值:
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 中,没有办法将右值原样传递到函数中。另外请注意,我们无法区分曾经是 const 限定左值的值和右值。
这就是我们通过 C++ 98 能够实现的、最接近上述通用转发函数 g
的方法。有关包括需要语言更改的解决方案在内的非常详细的讨论,请参阅 The Forwarding Problem。
现在,为了实际实现它,我们需要为每个参数数量(即 2^(Nmax+1) - 2^Nmin)提供 2^N 个重载(每个都有 const 和非 const 限定)。没错,这意味着编译时复杂度是 O(2^N),但由于系数很低,对于合理的参数数量(< 10)来说效果相当好。
函数对象适配器模板,其实例可以用左值和右值参数调用。右值参数被转发为引用到 const 的左值。
可以通过第二个、数值型的非类型模板参数来指定元数(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
可能被 const 限定的函数对象类型或其引用类型
f
可转换为 F
的对象
FA
类型 forward_adapter<F>
fa
FA
的实例对象,用 f
初始化
a0
...aN
传递给 fa
的参数
目标函数调用的结果类型必须是
boost::result_of
<F*(TA0 [const]&...TAN [const]&])>::type
其中 TA0
...TAN
表示 a0
...aN
的参数类型。
表达式 |
语义 |
---|---|
|
创建一个适配器,用 |
|
创建一个适配器,尝试使用 |
|
使用参数 |
宏 BOOST_FUNCTIONAL_FORWARD_ADAPTER_MAX_ARITY
可以被定义来设置最大调用元数。它默认为 6。
预处理时间:O(2^N),其中 N 是元数限制。编译时间:O(2^N),其中 N 取决于元数范围。运行时间:如果编译器内联,则为 O(0),否则为 O(1)。
函数对象适配器模板,其实例可以用左值和右值参数调用。所有参数都作为引用到 const 的左值进行转发,但引用包装器会被解包,并可能产生非 const 左值。
可以通过第二个、数值型的非类型模板参数来指定元数(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
可能被 const 限定的函数对象类型或其引用类型
f
可转换为 F
的对象
FA
类型 lightweight_forward_adapter<F>
fa
FA
的实例,用 f
初始化
a0
...aN
传递给 fa
的参数
目标函数调用的结果类型必须是
boost::result_of
<F*(TA0 [const]&...TAN [const]&])>::type
其中 TA0
...TAN
表示 a0
...aN
的参数类型。
表达式 |
语义 |
---|---|
|
创建一个适配器,用 |
|
创建一个适配器,尝试使用 |
|
使用 const 参数 |
宏 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 Preprocessor 库。没有它,我将不得不为此编写一个外部代码生成器。