There is a great paper on C++ for scientific computing where the author (T. Veldhuizen) suggests a traits-based approach to address type promotion. I have used such app
As in catscradle answer, decltype
and common_type
(and custom specializations for it), are probably good C++11 replacement for the need of conversion traits that Veldhuizen have in mind. However, it will still fall short if you need to be still very specific for the evaluation of a function that maps two types into one (binary operator). (In other words decltype
doesn't know about the mathematical domain of your problem).
My take is that you can resort to Boost.MPL maps http://www.boost.org/doc/libs/1_53_0/libs/mpl/doc/refmanual/map.html, this doesn't even require C++11, it is just that MPL wasn't written at that time:
#include<iostream>
#include<complex>
#include<typeinfo>
#include <boost/mpl/map.hpp>
#include <boost/mpl/at.hpp>
// all traits in one place, no need for MACROS or C++11, compile error if the case does not exist.
using namespace boost::mpl;
typedef map<
pair<pair<int, char>, int>,
pair<pair<int, float>, int>,
pair<pair<float, std::complex<float> >, std::complex<float> >
> mapped_promotion;
template<typename T1, typename T2>
void product(T1 a, T2 b) {
typedef typename at<mapped_promotion, pair<T1, T2> >::type T;
T ans = T(a) * T(b);
std::cout<<"received "
<<typeid(T1).name()<<"("<<a<<")"<<" * "
<<typeid(T2).name()<<"("<<b<<")"<<" ==> "
<<"returning "
<<typeid(T).name()<<"("<<ans<<")"<<std::endl;
}
int main() {
product(1, 'a');
product(1, 2.0f);
product(1.0f, std::complex<float>(1.0f, 2.0f));
return 0;
}
Another extra benefit of using MPL is that you can then easily move to Boost.Fusion
later, which usually is the case once you start dealing with "algebras" of types. And there is nothing to replace the functionality of Boost.Fusion in C++11 core language.
What follows is a more general solution, you can stop reading if the above was enough for your application, that combines MPL and decltype
and requires C++11 which allows for unspecified pair of types to default to the decltype solution if it is any good, the trick is to see if the return of the mpl::map
is the metatype void_
(pair not found).
...
#include <type_traits>
//specific promotions
using namespace boost::mpl;
typedef map<
pair<pair<int, char>, int>,
pair<pair<int, float>, int>,
pair<pair<float, std::complex<float> >, std::complex<float> >
> specific_mapped_promotion;
//promotion for unspecified combinations defaults to decltype type deduction.
template<class P1, class P2>
struct loose_mapped_promotion : std::conditional<
std::is_same<typename at<specific_mapped_promotion, pair<P1, P2> >::type, mpl_::void_>::value,
decltype( std::declval<P1>()*std::declval<P2>() ),
typename at<specific_mapped_promotion, pair<P1, P2> >::type
> {};
template<typename T1, typename T2>
void product(T1 a, T2 b) {
typedef typename loose_mapped_promotion<T1, T2>::type T;
T ans = T(a) * T(b);
...
}
int main() {
product(1.0, std::complex<double>(1.0f, 2.0f)); // now accepted, although no explicit trait was made
}
On a final note: it is apparently ok to overload std::common_type
for special cases, if you want to use it: http://www.cplusplus.com/reference/type_traits/common_type/
I think you can use decltype
for this:
template <typename T, typename U>
void product(T t, U u)
{
std::cout << typeid(decltype(t * u)).name() << std::endl;
}
Or with declval
:
#include <utility>
template <typename T, typename U>
void product()
{
std::cout << typeid(decltype(std::declval<T>() * std::declval<U>())).name() << std::endl;
}
EDIT For T ans = T(a) * T(b);
you can just use auto
, auto ans = T(a) * T(b);