Es gibt bereits viele gute Antworten, daher werden meine Antworten einen Teil Ihrer Frage beantworten. Ich nehme die Prämisse Ihrer Frage in den Hintergrund, da sich OOP und Funktionsmerkmale nicht gegenseitig ausschließen.
Wenn Sie C ++ 11 verwenden, enthält die Sprach- / Standardbibliothek viele dieser funktionalen Programmierfunktionen, die sich gut mit OOP kombinieren lassen. Natürlich bin ich mir nicht sicher, wie gut TMP von Ihrem Chef oder Ihren Mitarbeitern empfangen wird, aber der Punkt ist, dass Sie viele dieser Funktionen in irgendeiner Form in nicht funktionalen / OOP-Sprachen wie C ++ erhalten können.
Die Verwendung von Vorlagen mit Rekursion zur Kompilierungszeit basiert auf Ihren ersten 3 Punkten.
- Unveränderlichkeit
- Rekursion
- Mustervergleich
Da Template-Werte unveränderlich sind (Konstanten für die Kompilierungszeit), wird jede Iteration unter Verwendung von Rekursion ausgeführt, und die Verzweigung wird unter Verwendung von (mehr oder weniger) Musterabgleich in Form einer Überladungsauflösung ausgeführt.
Was die anderen Punkte betrifft, verwenden Sie std::bind
und, um std::function
eine teilweise Funktionsanwendung zu erhalten, und Funktionszeiger sind in die Sprache integriert. Aufrufbare Objekte sind Funktionsobjekte (sowie Teilfunktionsanwendungen). Beachten Sie, dass mit aufrufbaren Objekten diejenigen gemeint sind, die ihre definieren operator ()
.
Lazy Evaluation und reine Funktionen wären etwas schwieriger; Für reine Funktionen können Sie Lambda-Funktionen verwenden, die nur nach Wert erfassen. Dies ist jedoch nicht ideal.
Im Folgenden finden Sie ein Beispiel für die Verwendung der Rekursion zur Kompilierungszeit mit Teilfunktionsanwendung. Es ist ein etwas ausgeklügeltes Beispiel, aber es zeigt die meisten der obigen Punkte. Es bindet die Werte in einem gegebenen Tupel rekursiv an eine gegebene Funktion und erzeugt ein (aufrufbares) Funktionsobjekt
#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;
}