版权所有 © 2007, 2008 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
的参数类型。
表达式 |
语义 |
---|---|
|
创建一个适配器,使用 |
|
创建一个适配器,尝试使用 |
|
使用参数 |
可以定义宏 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
的参数类型。
表达式 |
语义 |
---|---|
|
创建一个适配器,使用 |
|
创建一个适配器,尝试使用 |
|
使用常量参数 |
可以定义宏 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 预处理器库。如果没有它,我最终会为此使用一个外部代码生成器。