【发布时间】:2012-04-13 16:46:22
【问题描述】:
我经常使用 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
}
【问题讨论】:
-
IIRC,如果您使用的是 Phoenix,您可以将结果存储在
boost::phoenix::function而不是boost::function中并减轻一些效率损失(boost::phoenix::function是 POD 类型,可以静态在编译时初始化)。 -
@ildjarn 感谢您对
boost::phoenix::function的提醒,这在许多情况下一定很有用。不过,我仍然对获取本机等效(运行时性能方面)的 lambda 函数捕获感兴趣。我不确定是否有可能达到这种生产质量,但我觉得追求很有趣。
标签: c++ boost lambda boost-phoenix boost-proto