#include <functional>
#include <iostream>

// handle mixed type operations by selecting a "winning" type to promote to
// By default this should be symmetric. 
template <class T, class U> struct promote : public promote<U,T> { };
template <class T> struct promote<T,T> { typedef T result_type; };
template <> struct promote<double,int>{ typedef double result_type; };

// get result type from a templated binary functor with one template parameter.
template <template <class> class F, class T, class U > 
struct get_result_type 
{ 
	typedef typename promote<T,U>::result_type result_type; 
};

// given a binary functor F with one template parameter, what template parameter
// should we instantiate it with if we want to call it on types T and U ?
template <template <class> class F, class T, class U > 
struct get_template_parameter
{ 
	typedef typename promote<T,U>::result_type result_type; 
};


template <template <class> class Base, class T > struct X : public Base<T> {};

template <template <class> class Base>
struct X<Base,void>
{
	template <class T>
	T operator() (const T& arg)
	{
		return Base<T>()(arg);
	}

	template <class T>
	T operator() (const T& left, const T& right)
	{
		return Base<T>()(left,right);
	}

	template <class T, class U>
	typename get_result_type<Base,T,U>::result_type operator() 
	(const T& left, const U& right)
	{
		return Base<get_template_parameter<Base,T,U>::result_type>()
			(left,right);
	}
};






int main(int argc, char** argv)
{
	X<multiplies,double> x;
	X<multiplies,void> y;
	X<plus,int> z;
	X<plus,void> t;


	std::cout << x(2,3.4) << std::endl;
	std::cout << y(2.,3.4) << std::endl;
	std::cout << y(2,3) << std::endl;
	std::cout << z(2,3) << std::endl;
	std::cout << t(2,3) << std::endl;
	std::cout << t(2.1,3.) << std::endl;

	// works because of promotes template
	std::cout << t(2.1,3) << std::endl;
	std::cout << t(2,3.4) << std::endl;

}