Já existem muitas respostas boas, portanto as minhas abordarão um subconjunto da sua pergunta; ou seja, eu me ofendo à premissa de sua pergunta, já que OOP e recursos funcionais não são mutuamente exclusivos.
Se você usa C ++ 11, existem muitos desses tipos de recursos de programação funcional incorporados à biblioteca de idiomas / padrão que sinergizam (muito) bem com o OOP. Obviamente, não tenho certeza de quão bem o TMP será recebido por seu chefe ou colegas de trabalho, mas o ponto é que você pode obter muitos desses recursos de uma forma ou de outra em linguagens / OOP não funcionais, como C ++.
O uso de modelos com recursão no tempo de compilação depende dos seus três primeiros pontos,
- Imutabilidade
- Recursão
- Correspondência de padrões
Como os valores do modelo são imutáveis (constantes em tempo de compilação), qualquer iteração é feita usando recursão e a ramificação é feita usando (mais ou menos) correspondência de padrões, na forma de resolução de sobrecarga.
Quanto aos outros pontos, o uso std::bind
e std::function
a aplicação parcial de funções são fornecidas, e os ponteiros de função estão embutidos no idioma. Objetos que podem ser chamados são objetos funcionais (assim como aplicativos de funções parciais). Observe que, por objetos que podem ser chamados, quero dizer aqueles que definem seus operator ()
.
Avaliação preguiçosa e funções puras seriam um pouco mais difíceis; para funções puras, você pode usar funções lambda que capturam apenas por valor, mas isso não é o ideal.
Por fim, aqui está um exemplo de uso de recursão em tempo de compilação com aplicativo de função parcial. É um exemplo um tanto artificial, mas demonstra a maioria dos pontos acima. Ele vincula recursivamente os valores de uma tupla a uma determinada função e gera um objeto de função (que pode ser chamado)
#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;
}