eno*_*ram 8 c++ lambda boost boost-phoenix boost-proto
我经常使用boost.lambda(和phoenix)在C++中定义lambda函数.我非常喜欢它们的多态属性,它们表示的简单性以及它们在C++中进行函数式编程的方式变得如此简单.在某些情况下,它甚至更清晰,更易读(如果您习惯于阅读它们),可以使用它们来定义小函数并在静态范围内命名它们.
存储这些类似于传统功能的功能的方法最多的是捕获它们 boost::function
const boost::function<double(double,double)> add = _1+_2;
Run Code Online (Sandbox Code Playgroud)
但问题是这样做的运行时效率低下.即使add这里的函数是无状态的,返回的lambda类型也不是空的并且它sizeof大于1(因此boost::function默认的ctor和copy ctor将涉及new).我真的怀疑编译器或boost方面有一种机制来检测这种无状态并生成相当于使用的代码:
double (* const add)(double,double) = _1+_2; //not valid right now
Run Code Online (Sandbox Code Playgroud)
当然可以使用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
}
Run Code Online (Sandbox Code Playgroud)
用gcc编译4.6.1该程序的输出是(无论优化级别如何):
25
Segmentation fault
Run Code Online (Sandbox Code Playgroud)
正如所料.在这里,我保留一个指向lambda表达式类型的静态指针(尽可能为const用于优化目的)并将其初始化为NULL.这样,如果您尝试使用state"静态化"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
}
Run Code Online (Sandbox Code Playgroud)
Eri*_*ler 11
你说你喜欢Boost的lambda函数的多态性,但你没有在上面的代码中使用该属性.我的建议:使用C++ 11 lambda.无状态的已经有一个隐式转换为原始函数指针.这就是你要找的东西,IMO.
=== UPDATE ===
虽然通过空指针调用成员函数是一个可怕的想法(不要这样做,你会失明),你可以默认构造一个与原始相同类型的新 lambda对象.如果你将它与上面#2中的建议结合起来,你可以得到你想要的东西.这是代码:
#include <iostream>
#include <type_traits>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/and.hpp>
#include <boost/phoenix.hpp>
namespace detail
{
using namespace boost::proto;
namespace mpl = boost::mpl;
struct is_stateless
: or_<
when<terminal<_>, std::is_empty<_value>()>,
otherwise<
fold<_, mpl::true_(), mpl::and_<_state, is_stateless>()>
>
>
{};
template<typename Lambda>
struct static_lambda
{
template<typename Sig>
struct impl;
template<typename Ret, typename Arg0, typename Arg1>
struct impl<Ret(Arg0, Arg1)>
{
static Ret apply(Arg0 arg0, Arg1 arg1)
{
return Lambda()(arg0, arg1);
}
};
template<typename Fun>
operator Fun*() const
{
return &impl<Fun>::apply;
}
};
template<typename Lambda>
inline static_lambda<Lambda> make_static(Lambda const &l)
{
static_assert(
boost::result_of<is_stateless(Lambda)>::type::value,
"Lambda is not stateless"
);
return static_lambda<Lambda>();
}
}
using detail::make_static;
int main()
{
using namespace boost::phoenix;
using namespace placeholders;
int c=5;
int (*add)(int,int) = make_static(_1+_2);
// We can even define arrays with the following syntax
static double (*const func_array[])(double,double) =
{
make_static(_1+_2),
make_static(_1*_2)
};
std::cout << func_array[0](10,15) << "\n";
std::cout << func_array[1](10,15);
// If you try to create a stateless lambda from a lambda
// with state, you trigger a static assertion:
int (*oops)(int,int) = make_static(_1+_2+42); // ERROR, not stateless
}
Run Code Online (Sandbox Code Playgroud)
免责声明:我不是凤凰城的作者.我不知道所有无状态lambda的默认构造能力是否得到保证.
用MSVC-10.0测试.
请享用!
| 归档时间: |
|
| 查看次数: |
873 次 |
| 最近记录: |