Funciones estáticas de boost.lambda o boost.phoenix

Regularmente uso boost.lambda (y phoenix) para definir funciones lambda en C ++. Realmente me gusta su propiedad polimórfica, la simplicidad de su representación y la forma en que hacen que la programación funcional en C ++ sea mucho más fácil. En algunos casos, es aún más limpio y más legible (si está acostumbrado a leerlos) usarlos para definir funciones pequeñas y nombrarlas en el ámbito estático.

La forma de almacenar estos funcionales que se parecen más a las funciones convencionales es capturarlos en unboost::function

<code>const boost::function<double(double,double)> add = _1+_2;
</code>

Pero el problema es la ineficiencia en el tiempo de ejecución de hacerlo. A pesar deadd La función aquí no tiene estado, el tipo lambda devuelto no está vacío y susizeof es mayor que 1 (entoncesboost::function ctor por defecto y copia ctor implicaránew). Realmente dudo que exista un mecanismo del lado del compilador o del impulso para detectar esta falta de estado y generar código que sea equivalente a usar:

<code>double (* const add)(double,double) = _1+_2; //not valid right now
</code>

Uno podría, por supuesto, utilizar el c ++ 11auto, pero entonces la variable no se puede pasar por contextos sin plantillas. Finalmente he logrado hacer casi lo que quiero, usando el siguiente enfoque:

<code>#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
}
</code>

Compilado con gcc 4.6.1 El resultado de este programa es (independientemente del nivel de optimización):

<code>25
Segmentation fault
</code>

como se esperaba. Aquí, mantengo un puntero estático al tipo de expresión lambda (lo más constante posible para fines de optimización) y lo inicializo paraNULL. De esta manera, si intenta "estaticizar" una expresión lambda con estado, está seguro de obtener un error de tiempo de ejecución. Y si usted estaticiza una expresión lambda genuinamente sin estado, todo funciona.

A la (s) pregunta (s):

El método parece un poco sucio, ¿puede pensar en alguna circunstancia, o en el supuesto de que el compilador haga que esto se comporte mal?

¿Se puede pensar en alguna forma en que al intentar esto se genere un error de compilación en lugar de un error de seguridad cuando la expresión lambda tiene un estado?

EDITAR después de la respuesta de Eric Niebler:

<code>#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
}
</code>

Respuestas a la pregunta(1)

Su respuesta a la pregunta