Ya hay muchas buenas respuestas, por lo que la mía abordará un subconjunto de su pregunta; a saber, considero la premisa de su pregunta, ya que las características funcionales y OOP no son mutuamente excluyentes.
Si usa C ++ 11, hay muchas de estas características de programación funcional integradas en el lenguaje / biblioteca estándar que sinergizan (bastante) bien con OOP. Por supuesto, no estoy seguro de qué tan bien TMP será recibido por su jefe o compañeros de trabajo, pero el punto es que puede obtener muchas de estas características de una forma u otra en lenguajes no funcionales / OOP, como C ++.
El uso de plantillas con recursión en tiempo de compilación depende de sus primeros 3 puntos,
- Inmutabilidad
- Recursividad
- La coincidencia de patrones
En que los valores de plantilla son inmutables (constantes de tiempo de compilación), cualquier iteración se realiza mediante recursión, y la ramificación se realiza utilizando (más o menos) coincidencia de patrones, en forma de resolución de sobrecarga.
En cuanto a los otros puntos, el uso de std::bind
y std::function
le brinda una aplicación de función parcial, y los punteros de función están integrados en el lenguaje. Los objetos invocables son objetos funcionales (así como una aplicación de función parcial). Tenga en cuenta que por objetos invocables, me refiero a los que definen sus operator ()
.
La evaluación perezosa y las funciones puras serían un poco más difíciles; para funciones puras, puede usar funciones lambda que solo capturan por valor, pero esto no es ideal.
Por último, aquí hay un ejemplo del uso de la recursión en tiempo de compilación con la aplicación de función parcial. Es un ejemplo un tanto artificial, pero demuestra la mayoría de los puntos anteriores. Enlazará recursivamente los valores en una tupla dada a una función dada y generará un objeto de función (invocable)
#include <iostream>
#include <functional>
//holds a compile-time index sequence
template<std::size_t ... >
struct index_seq
{};
//builds the index_seq<...> struct with the indices (boils down to compile-time indexing)
template<std::size_t N, std::size_t ... Seq>
struct gen_indices
: gen_indices<N-1, N-1, Seq ... >
{};
template<std::size_t ... Seq>
struct gen_indices<0, Seq ... >
{
typedef index_seq<Seq ... > type;
};
template <typename RType>
struct bind_to_fcn
{
template <class Fcn, class ... Args>
std::function<RType()> fcn_bind(Fcn fcn, std::tuple<Args...> params)
{
return bindFunc(typename gen_indices<sizeof...(Args)>::type(), fcn, params);
}
template<std::size_t ... Seq, class Fcn, class ... Args>
std::function<RType()> bindFunc(index_seq<Seq...>, Fcn fcn, std::tuple<Args...> params)
{
return std::bind(fcn, std::get<Seq>(params) ...);
}
};
//some arbitrary testing function to use
double foo(int x, float y, double z)
{
return x + y + z;
}
int main(void)
{
//some tuple of parameters to use in the function call
std::tuple<int, float, double> t = std::make_tuple(1, 2.04, 0.1);
typedef double(*SumFcn)(int,float,double);
bind_to_fcn<double> binder;
auto other_fcn_obj = binder.fcn_bind<SumFcn>(foo, t);
std::cout << other_fcn_obj() << std::endl;
}