我经常使用 boost.lambda(和 phoenix)在 C++ 中定义 lambda 函数。我真的很喜欢它们的多态属性、它们的简单表示以及它们使 C++ 中的函数式编程变得如此容易的方式。在某些情况下,使用它们来定义小函数并在静态范围内命名它们会更干净、更易读(如果您习惯阅读它们)。
存储这些与传统函数最相似的函数的方法是将它们捕获在一个boost::function
const boost::function<double(double,double)> add = _1+_2;
但问题是这样做的运行时效率低下。尽管add
这里的函数是无状态的,但返回的 lambda 类型不是空的,并且sizeof
大于 1(因此boost::function
默认 ctor 和复制 ctor 都会涉及new
)。我真的怀疑编译器或 boost 方面是否有一种机制来检测这种无状态并生成相当于使用的代码:
double (* const add)(double,double) = _1+_2; //not valid right now
当然可以使用 c++11 auto
,但是变量不能在非模板上下文中传递。使用以下方法,我终于设法做几乎我想做的事:
#include <boost/lambda/lambda.hpp>
using namespace boost::lambda;
#include <boost/type_traits.hpp>
#include <boost/utility/result_of.hpp>
using namespace boost;
template <class T>
struct static_lambda {
static const T* const t;
// Define a static function that calls the functional t
template <class arg1type, class arg2type>
static typename result_of<T(arg1type,arg2type)>::type
apply(arg1type arg1,arg2type arg2){
return (*t)(arg1,arg2);
}
// The conversion operator
template<class func_type>
operator func_type*() {
typedef typename function_traits<func_type>::arg1_type arg1type;
typedef typename function_traits<func_type>::arg2_type arg2type;
return &static_lambda<T>::apply<arg1type,arg2type>;
}
};
template <class T>
const T* const static_lambda<T>::t = 0;
template <class T>
static_lambda<T> make_static(T t) {return static_lambda<T>();}
#include <iostream>
#include <cstdio>
int main() {
int c=5;
int (*add) (int,int) = make_static(_1+_2);
// We can even define arrays with the following syntax
double (*const func_array[])(double,double) = {make_static(_1+_2),make_static(_1*_2*ref(c))};
std::cout<<func_array[0](10,15)<<"\n";
std::fflush(stdout);
std::cout<<func_array[1](10,15); // should cause segmentation fault since func_array[1] has state
}
使用 gcc 4.6.1 编译该程序的输出是(无论优化级别如何):
25
Segmentation fault
正如预期的那样。在这里,我保留了一个指向 lambda 表达式类型的静态指针(出于优化目的,尽可能为 const)并将其初始化为NULL
. 这样,如果您尝试“静态化”带有状态的 lambda 表达式,您肯定会遇到运行时错误。如果你静态化一个真正无状态的 lambda 表达式,一切都会解决。
关于问题:
该方法似乎有点脏,您能想到任何会导致这种行为不端的情况或编译器假设(预期行为:如果 lambda 是无状态的,则可以正常工作,否则会出现段错误)。
当 lambda 表达式有状态时,你能想到任何尝试这样做会导致编译器错误而不是段错误的方法吗?
在 Eric Niebler 回答后编辑:
#include <boost/phoenix.hpp>
using namespace boost::phoenix;
using namespace boost::phoenix::arg_names;
#include <boost/type_traits.hpp>
#include <boost/utility/result_of.hpp>
using boost::function_traits;
template <class T>
struct static_lambda {
static const T t;
// A static function that simply applies t
template <class arg1type, class arg2type>
static typename boost::result_of<T(arg1type,arg2type)>::type
apply(arg1type arg1,arg2type arg2){
return t(arg1,arg2);
}
// Conversion to a function pointer
template<class func_type>
operator func_type*() {
typedef typename function_traits<func_type>::arg1_type arg1type;
typedef typename function_traits<func_type>::arg2_type arg2type;
return &static_lambda<T>::apply<arg1type,arg2type>;
}
};
template <class T>
const T static_lambda<T>::t; // Default initialize the functional
template <class T>
static_lambda<T> make_static(T t) {return static_lambda<T>();}
#include <iostream>
#include <cstdio>
int main() {
int (*add) (int,int) = make_static(_1+_2);
std::cout<<add(10,15)<<"\n";
int c=5;
// int (*add_with_ref) (int,int) = make_static(_1+_2+ref(c)); causes compiler error as desired
}