私は定期的にboost.lambda(およびフェニックス)を使用してC++でラムダ関数を定義しています。私はそれらの多態性、表現の単純さ、そしてC++での関数型プログラミングを非常に簡単にする方法が本当に好きです。場合によっては、小さな関数を定義し、静的スコープで名前を付けるためにそれらを使用する方が、よりクリーンで読みやすくなります(それらを読み取ることに慣れている場合)。
従来の関数に最も類似しているこれらの汎関数を格納する方法は、それらをキャプチャすることです。boost::function
const boost::function<double(double,double)> add = _1+_2;
しかし、問題はそうすることの実行時の非効率性です。ここでのadd
関数はステートレスですが、返されるラムダ型は空ではなくsizeof
、1より大きいです(したがって、boost::function
デフォルトのctorとcopy ctorにはnew
)が含まれます。コンパイラ側またはブースト側から、このステートレス性を検出して、次を使用するのと同等のコードを生成するメカニズムがあるかどうかは本当に疑わしいです。
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
予想通り。ここでは、ラムダ式タイプへの静的ポインターを保持し(最適化の目的で可能な限りconst)、それをに初期化しNULL
ます。このように、状態を使用してラムダ式を「静的化」しようとすると、実行時エラーが発生します。そして、真にステートレスなラムダ式を静的化すると、すべてがうまくいきます。
質問について:
メソッドは少し汚いようですが、これを誤動作させる状況やコンパイラの仮定を考えてみてください(予期される動作:ラムダがステートレスの場合は正常に動作し、それ以外の場合はセグメンテーション違反)。
これを試みると、ラムダ式に状態がある場合に、セグメンテーション違反ではなくコンパイラエラーが発生する方法を考えられますか?
エリック・ニーブラーの答えの後で編集してください:
#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
}