Для этого вы можете использовать оператор ?:
. Это даст вам общий тип между двумя типами. Во-первых, если два типа одинаковы, у вас все хорошо. Затем, если типы различаются, вы вызываете ?:
и смотрите, какой тип вы получите.
Вам необходимо в особом случае не продвигать типы char
, short
и их неподписанные / подписанные версии, поскольку применительно к двум таким операндам различных типов результат не будет ни одним из них. Вам также необходимо позаботиться о случае, когда два класса могут быть преобразованы в повышенные арифметические типы. Чтобы понять это правильно, мы проверяем, является ли результат ?:
повышенным арифметическим типом (в духе пункта 13.6
), и затем используем этот тип.
// 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;
};
Если ваши Complex<T>
типы могут быть преобразованы друг в друга, ?:
не найдет общий тип. Вы могли бы специализировать promote
, чтобы рассказать ему, как определить общий тип двух Complex<T>
:
template<typename T, typename U>
struct promote<Complex<T>, Complex<U>> {
typedef Complex<typename promote<T, U>::type> type;
};
Простое использование:
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;
}
Обратите внимание, что для реальных применений лучше всего поймать несколько случаев, которые я упустил для простоты, например, <const int, int>
должен обрабатываться аналогично <T, T>
(вам лучше всего использовать первую полосу const
и volatile
и преобразуйте T[N]
в T*
и T&
в T
и затем делегируйте действительный шаблон promote
- т.е. сделайте boost::remove_cv<boost::decay<T>>::type
для A
и B
перед их делегированием). Если вы этого не сделаете, для этих случаев вызов на номер check
окажется неопределенным.