// (by Ariel Badichi) #include <iostream>
#include <boost/type_traits/is_empty.hpp> #include <boost/mpl/bool.hpp>
namespace mpl = boost::mpl;
template<typename F, bool F_empty, typename G, bool G_empty> class storage;
template<typename F, typename G> class storage<F, false, G, false> // neither F nor G is empty { protected: storage(const F & f, const G & g) : f(f) , g(g) { }
const F & get_f() { return f; } const G & get_g() { return g; }
private: F f; G g; };
template<typename F, typename G> class storage<F, false, G, true> // G is empty : private G { protected: storage(const F & f, const G & g) : G(g) , f(f) { }
const F & get_f() { return f; } const G & get_g() { return *this; }
private: F f; };
template<typename F, typename G> class storage<F, true, G, false> // F is empty : private F { protected: storage(const F & f, const G & g) : F(f) , g(g) { }
const F & get_f() { return *this; } const G & get_g() { return g; }
private: G g; };
template<typename F, typename G, bool BothEmpty?> class storage_both_empty;
template<typename F, typename G> class storage_both_empty<F, G, false> : storage<F, true, G, false> { protected: storage_both_empty(const F & f, const G & g) : storage<F, true, G, false>(f, g) { } };
template<typename F, typename G> class storage_both_empty<F, G, true> : private F , private G { protected: storage_both_empty(const F & f, const G & g) : F(f) , G(g) { }
const F & get_f() { return *this; } const G & get_g() { return *this; } };
template<typename F, typename G> struct use_both_as_base #ifdef NO_MI_EBO : mpl::false_ #else : mpl::true_ #endif { };
template<typename F, typename G> class storage<F, true, G, true> : storage_both_empty<F, G, use_both_as_base<F, G>::value> { protected: typedef storage_both_empty<F, G, use_both_as_base<F, G>::value> base;
storage(const F & f, const G & g) : base(f, g) { } };
//
template<typename R, typename F, typename G> class compose_fg : storage<F, boost::is_empty<F>::value, G, boost::is_empty<G>::value> { typedef storage< F, boost::is_empty<F>::value, G, boost::is_empty<G>::value > base;
public: compose_fg(const F & f, const G & g) : base(f, g) { }
template<typename T> R operator() (const T & x) const { const F & f = this->get_f(); const G & g = this->get_g(); return f(g(x)); } };
struct Empty { int operator() (int x) const { return x; } };
struct Empty2 { int operator() (int x) const { return x; } };
struct Nonempty { Nonempty(int m) : m(m) {} int operator() (int x) const { return x + m; } int m; };
template<typename R, typename F, typename G> compose_fg<R, F, G> compose(const F & f, const G & g) { return compose_fg<R, F, G>(f, g); }
int main() { Nonempty nonempty(1); Empty empty; Empty2 empty2;
std::cout << sizeof(compose<int>(nonempty, nonempty)) << "\n"; std::cout << sizeof(compose<int>(nonempty, empty)) << "\n"; std::cout << sizeof(compose<int>(empty, nonempty)) << "\n"; std::cout << sizeof(compose<int>(empty2, empty)) << "\n";
return 0; }