Type traits to get result of promotion from operator
A follow up question for this is there a good way to do this with only using C++98 no boost?
Type traits to get result of promotion from operator
A follow up question for this is there a good way to do this with only using C++98 no boost?
So long as you only need to worry about the standard built-in types, you can do it like so:
template <int> struct plus_helper;
template <> struct plus_helper< 1> { typedef char type; };
template <> struct plus_helper< 2> { typedef signed char type; };
template <> struct plus_helper< 3> { typedef unsigned char type; };
template <> struct plus_helper< 4> { typedef short type; };
template <> struct plus_helper< 5> { typedef unsigned short type; };
template <> struct plus_helper< 6> { typedef int type; };
template <> struct plus_helper< 7> { typedef unsigned int type; };
template <> struct plus_helper< 8> { typedef long type; };
template <> struct plus_helper< 9> { typedef unsigned long type; };
template <> struct plus_helper<10> { typedef float type; };
template <> struct plus_helper<11> { typedef double type; };
template <> struct plus_helper<12> { typedef long double type; };
template <> struct plus_helper<13> { typedef wchar_t type; };
template <typename T1, typename T2>
struct plus {
private:
static char (&f(char))[1];
static char (&f(signed char))[2];
static char (&f(unsigned char))[3];
static char (&f(short))[4];
static char (&f(unsigned short))[5];
static char (&f(int))[6];
static char (&f(unsigned int))[7];
static char (&f(long))[8];
static char (&f(unsigned long))[9];
static char (&f(float))[10];
static char (&f(double))[11];
static char (&f(long double))[12];
static char (&f(wchar_t))[13];
public:
typedef typename plus_helper<sizeof(f(*(T1*)0 + *(T2*)0))>::type type;
};
template <typename T1, typename T2>
struct plus<T1 *, T2> {
typedef T1 *type;
};
template <typename T1, typename T2>
struct plus<T1, T2 *> {
typedef T2 *type;
};
Note that I've required that T1
and T2
can be added. No error is necessarily given if they cannot be. Note also that I've not omitted the shorter-than-int
types. They can never be returned by the built-in +
operator, but they're valid return types for a custom operator+
.
Unfortunately, if you also need to worry about other types, such as custom structures, this approach isn't going to work, and while I would be happy to be proven wrong, I don't think it's possible to make that work.
Probably not the best way, but this is what i got...
template <bool value,typename T,typename J>
struct conditional{typedef T type;};
template <typename T,typename J>
struct conditional<false,T,J>{typedef J type;};
template <typename T, typename J>
struct is_equal
{
enum {value = false};
};
template <typename T>
struct is_equal<T,T>
{
enum {value = true};
};
namespace
{
template <typename T, typename J, typename K, typename L>
struct math_t
{
typedef typename conditional<
is_equal<T,K>::value || is_equal<J,K>::value,
K,
L>::type type;
};
}
template <typename T, typename J>
struct math_type
{
typedef typename math_t<T,J,long double,
typename math_t<T,J,double,
typename math_t<T,J,float,
typename math_t<T,J,long long unsigned int,
typename math_t<T,J,long long int,
typename math_t<T,J,long unsigned int,
typename math_t<T,J,long int,
typename math_t<T,J,unsigned int,
int>::type>::type>::type>::type>::type>::type>::type>::type type;
};
I covered a similar issue here: https://stackoverflow.com/a/2450157/34509 . Repasting my code because this seems to be what you are after. Please see the referred answer for a detailed explanation
// typedef eiher to A or B, depending on what integer is passed
template<int, typename A, typename B>
struct cond;
#define CCASE(N, typed) \
template<typename A, typename B> \
struct cond<N, A, B> { \
typedef typed type; \
}
CCASE(1, A); CCASE(2, B);
CCASE(3, int); CCASE(4, unsigned int);
CCASE(5, long); CCASE(6, unsigned long);
CCASE(7, float); CCASE(8, double);
CCASE(9, long double);
#undef CCASE
// for a better syntax...
template<typename T> struct identity { typedef T type; };
// different type => figure out common type
template<typename A, typename B>
struct promote {
private:
static A a;
static B b;
// in case A or B is a promoted arithmetic type, the template
// will make it less preferred than the nontemplates below
template<typename T>
static identity<char[1]>::type &check(A, T);
template<typename T>
static identity<char[2]>::type &check(B, T);
// "promoted arithmetic types"
static identity<char[3]>::type &check(int, int);
static identity<char[4]>::type &check(unsigned int, int);
static identity<char[5]>::type &check(long, int);
static identity<char[6]>::type &check(unsigned long, int);
static identity<char[7]>::type &check(float, int);
static identity<char[8]>::type &check(double, int);
static identity<char[9]>::type &check(long double, int);
public:
typedef typename cond<sizeof check(0 ? a : b, 0), A, B>::type
type;
};
// same type => finished
template<typename A>
struct promote<A, A> {
typedef A type;
};
For example
int main() {
promote<char, short>::type a;
int *p0 = &a;
promote<float, double>::type b;
double *p1 = &b;
promote<char*, string>::type c;
string *p2 = &c;
}