Funções estáticas de boost.lambda ou boost.phoenix

Eu uso regularmente o boost.lambda (e phoenix) para definir funções lambda em C ++. Eu realmente gosto de sua propriedade polimórfica, a simplicidade de sua representação e a maneira como facilitam a programação funcional em C ++. Em alguns casos, é ainda mais claro e legível (se você está acostumado a lê-los) para usá-los para definir pequenas funções e nomeá-las no escopo estático.

A maneira de armazenar esses funcionais que mais se assemelham a funções convencionais é capturá-los deboost::function

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

Mas o problema é a ineficiência do tempo de execução. Mesmo que aadd função aqui é stateless, o tipo lambda retornado não está vazio e suasizeof é maior que 1 (entãoboost::function Ctor padrão e diretor de cópia envolveránew). Eu realmente duvido que exista um mecanismo do lado do compilador ou do boost para detectar essa apatridia e gerar código, o que equivale a usar:

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

Pode-se, claro, usar o c ++ 11auto, mas a variável não pode ser passada em contextos não-modelados. Eu finalmente consegui fazer quase o que eu quero, usando a seguinte abordagem:

<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 com gcc 4.6.1 A saída deste programa é (independentemente do nível de otimização):

<code>25
Segmentation fault
</code>

como esperado. Aqui, eu estou mantendo um ponteiro estático para o tipo de expressão lambda (como const quanto possível para fins de otimização) e inicializando-o paraNULL. Dessa forma, se você tentar "estabilizar" uma expressão lambda com estado, você com certeza obterá um erro de tempo de execução. E se você tornar uma expressão lambda genuinamente sem estado, tudo funcionará.

Para a (s) pergunta (s):

O método parece um pouco sujo, você pode pensar em qualquer circunstância, ou suposição do compilador que fará com que isso se comporte mal (comportamento esperado: funciona bem se o lambda é stateless, segfault caso contrário).

Você pode pensar em alguma maneira que tentar isso causará um erro do compilador em vez de um segfault quando a expressão lambda tiver estado?

EDIT depois da resposta 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>

questionAnswers(1)

yourAnswerToTheQuestion